-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEZH8oZUiU471FcZm+ONu9yGCSaT4FAmS38qMACgkQONu9yGCS aT56yQ//ZuDuw8Ev3HISVgZhE9FpuXC1RSYXiMCAvwA9rH3KnJ4wKVPEhEWLy9P4 jdJaatSLbLOvA7ME7JnwZxz2qahjBxo1tpx6u2S3zrzz4UlAPNLwCxTxxp4X07VI 3fBNvsmucqFSayCrA8t9xgkaJizuCvHZm7eSoyVIigPwbB5igc2b+bNSRcx1Zo+j SHl4Y4nGK8a47XU9RSlDLVKow0/6rrQLHQ9DLpxACArRHw3h451vD0DMcgOuU/Uv 6qq9u3COcdVw3oc5VENu9XklPmvQkxo3RaCUHyRadVstuc0H/BBUDvEhPn5PcVOV EdBWlTjmhsQo0aUziK4kotLNeX1VRgKa+rrIUBJn68OHv1SRRPZU/eJ8hkL81dCi FDPzXDOszixO7pPv1jj7O9kNcwKPuiHPmdaNPCY6jviOHhZnAEub44DpQamxWvU/ kb5MZRRY72wt9iWeI3kscCCSbf6eyjlmDMoYIeLuYn10n7gIDU80eUOBl9bqEsz/ X+OUxaY+XuKbCoucpNmSHHLmynJ5D0CXhl/5qnlgMoSo4UJ5BUIMj2e3ZqsKLfrR e/09MCRX79y9J+TxUunnQZfq5vBlH1tRsvUyhIfYfW4AaC9BrkOL2XZviQldKY6x FUmsxh62O3iGRtLOWDKQA5MwoJuD54qVcHr1iidWkO2G8T3ctCc= =kyUh -----END PGP SIGNATURE----- Merge 6.1.39 into android14-6.1-lts Changes in 6.1.39 drm: use mgr->dev in drm_dbg_kms in drm_dp_add_payload_part2 fs: pipe: reveal missing function protoypes block: Fix the type of the second bdev_op_is_zoned_write() argument erofs: clean up cached I/O strategies erofs: avoid tagged pointers to mark sync decompression erofs: remove tagged pointer helpers erofs: move zdata.h into zdata.c erofs: kill hooked chains to avoid loops on deduplicated compressed images x86/resctrl: Only show tasks' pid in current pid namespace blk-iocost: use spin_lock_irqsave in adjust_inuse_and_calc_cost x86/sev: Fix calculation of end address based on number of pages virt: sevguest: Add CONFIG_CRYPTO dependency blk-mq: fix potential io hang by wrong 'wake_batch' lockd: drop inappropriate svc_get() from locked_get() nvme-auth: rename __nvme_auth_[reset|free] to nvme_auth[reset|free]_dhchap nvme-auth: rename authentication work elements nvme-auth: remove symbol export from nvme_auth_reset nvme-auth: no need to reset chap contexts on re-authentication nvme-core: fix memory leak in dhchap_secret_store nvme-core: fix memory leak in dhchap_ctrl_secret nvme-auth: don't ignore key generation failures when initializing ctrl keys nvme-core: add missing fault-injection cleanup nvme-core: fix dev_pm_qos memleak md/raid10: check slab-out-of-bounds in md_bitmap_get_counter md/raid10: fix overflow of md/safe_mode_delay md/raid10: fix wrong setting of max_corr_read_errors md/raid10: fix null-ptr-deref of mreplace in raid10_sync_request md/raid10: fix io loss while replacement replace rdev md/raid1-10: factor out a helper to add bio to plug md/raid1-10: factor out a helper to submit normal write md/raid1-10: submit write io directly if bitmap is not enabled block: fix blktrace debugfs entries leakage irqchip/stm32-exti: Fix warning on initialized field overwritten irqchip/jcore-aic: Fix missing allocation of IRQ descriptors svcrdma: Prevent page release when nothing was received erofs: simplify iloc() erofs: fix compact 4B support for 16k block size posix-timers: Prevent RT livelock in itimer_delete() tick/rcu: Fix bogus ratelimit condition tracing/timer: Add missing hrtimer modes to decode_hrtimer_mode(). clocksource/drivers/cadence-ttc: Fix memory leak in ttc_timer_probe PM: domains: fix integer overflow issues in genpd_parse_state() perf/arm-cmn: Fix DTC reset x86/mm: Allow guest.enc_status_change_prepare() to fail x86/tdx: Fix race between set_memory_encrypted() and load_unaligned_zeropad() drivers/perf: hisi: Don't migrate perf to the CPU going to teardown powercap: RAPL: Fix CONFIG_IOSF_MBI dependency PM: domains: Move the verification of in-params from genpd_add_device() ARM: 9303/1: kprobes: avoid missing-declaration warnings cpufreq: intel_pstate: Fix energy_performance_preference for passive thermal/drivers/sun8i: Fix some error handling paths in sun8i_ths_probe() rcu: Make rcu_cpu_starting() rely on interrupts being disabled rcu-tasks: Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs rcutorture: Correct name of use_softirq module parameter rcuscale: Move shutdown from wait_event() to wait_event_idle() rcu/rcuscale: Move rcu_scale_*() after kfree_scale_cleanup() rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale kselftest: vDSO: Fix accumulation of uninitialized ret when CLOCK_REALTIME is undefined perf/ibs: Fix interface via core pmu events x86/mm: Fix __swp_entry_to_pte() for Xen PV guests locking/atomic: arm: fix sync ops evm: Complete description of evm_inode_setattr() evm: Fix build warnings ima: Fix build warnings pstore/ram: Add check for kstrdup igc: Enable and fix RX hash usage by netstack wifi: ath9k: fix AR9003 mac hardware hang check register offset calculation wifi: ath9k: avoid referencing uninit memory in ath9k_wmi_ctrl_rx libbpf: btf_dump_type_data_check_overflow needs to consider BTF_MEMBER_BITFIELD_SIZE samples/bpf: Fix buffer overflow in tcp_basertt spi: spi-geni-qcom: Correct CS_TOGGLE bit in SPI_TRANS_CFG wifi: wilc1000: fix for absent RSN capabilities WFA testcase wifi: mwifiex: Fix the size of a memory allocation in mwifiex_ret_802_11_scan() sctp: add bpf_bypass_getsockopt proto callback libbpf: fix offsetof() and container_of() to work with CO-RE bpf: Don't EFAULT for {g,s}setsockopt with wrong optlen spi: dw: Round of n_bytes to power of 2 nfc: llcp: fix possible use of uninitialized variable in nfc_llcp_send_connect() bpftool: JIT limited misreported as negative value on aarch64 bpf: Remove bpf trampoline selector bpf: Fix memleak due to fentry attach failure selftests/bpf: Do not use sign-file as testcase regulator: core: Fix more error checking for debugfs_create_dir() regulator: core: Streamline debugfs operations wifi: orinoco: Fix an error handling path in spectrum_cs_probe() wifi: orinoco: Fix an error handling path in orinoco_cs_probe() wifi: atmel: Fix an error handling path in atmel_probe() wifi: wl3501_cs: Fix an error handling path in wl3501_probe() wifi: ray_cs: Fix an error handling path in ray_probe() wifi: ath9k: don't allow to overwrite ENDPOINT0 attributes samples/bpf: xdp1 and xdp2 reduce XDPBUFSIZE to 60 wifi: ath10k: Trigger STA disconnect after reconfig complete on hardware restart wifi: mac80211: recalc min chandef for new STA links selftests/bpf: Fix check_mtu using wrong variable type wifi: rsi: Do not configure WoWlan in shutdown hook if not enabled wifi: rsi: Do not set MMC_PM_KEEP_POWER in shutdown ice: handle extts in the miscellaneous interrupt thread selftests: cgroup: fix unexpected failure on test_memcg_low watchdog/perf: define dummy watchdog_update_hrtimer_threshold() on correct config watchdog/perf: more properly prevent false positives with turbo modes kexec: fix a memory leak in crash_shrink_memory() mmc: mediatek: Avoid ugly error message when SDIO wakeup IRQ isn't used memstick r592: make memstick_debug_get_tpc_name() static wifi: ath9k: Fix possible stall on ath9k_txq_list_has_key() wifi: mac80211: Fix permissions for valid_links debugfs entry rtnetlink: extend RTEXT_FILTER_SKIP_STATS to IFLA_VF_INFO wifi: ath11k: Add missing check for ioremap wifi: iwlwifi: pull from TXQs with softirqs disabled wifi: iwlwifi: pcie: fix NULL pointer dereference in iwl_pcie_irq_rx_msix_handler() wifi: mac80211: Remove "Missing iftype sband data/EHT cap" spam wifi: cfg80211: rewrite merging of inherited elements wifi: cfg80211: drop incorrect nontransmitted BSS update code wifi: cfg80211: fix regulatory disconnect with OCB/NAN wifi: cfg80211/mac80211: Fix ML element common size calculation wifi: ieee80211: Fix the common size calculation for reconfiguration ML mmc: Add MMC_QUIRK_BROKEN_SD_CACHE for Kingston Canvas Go Plus from 11/2019 wifi: iwlwifi: mvm: indicate HW decrypt for beacon protection wifi: ath9k: convert msecs to jiffies where needed bpf: Factor out socket lookup functions for the TC hookpoint. bpf: Call __bpf_sk_lookup()/__bpf_skc_lookup() directly via TC hookpoint bpf: Fix bpf socket lookup from tc/xdp to respect socket VRF bindings can: length: fix bitstuffing count can: kvaser_pciefd: Add function to set skb hwtstamps can: kvaser_pciefd: Set hardware timestamp on transmitted packets net: stmmac: fix double serdes powerdown netlink: fix potential deadlock in netlink_set_err() netlink: do not hard code device address lenth in fdb dumps bonding: do not assume skb mac_header is set selftests: rtnetlink: remove netdevsim device after ipsec offload test gtp: Fix use-after-free in __gtp_encap_destroy(). net: axienet: Move reset before 64-bit DMA detection ocfs2: Fix use of slab data with sendpage sfc: fix crash when reading stats while NIC is resetting net: nfc: Fix use-after-free caused by nfc_llcp_find_local lib/ts_bm: reset initial match offset for every block of text netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one netfilter: nf_conntrack_sip: fix the ct_sip_parse_numerical_param() return value. ipvlan: Fix return value of ipvlan_queue_xmit() netlink: Add __sock_i_ino() for __netlink_diag_dump(). drm/amd/display: Add logging for display MALL refresh setting radeon: avoid double free in ci_dpm_init() drm/amd/display: Explicitly specify update type per plane info change drm/bridge: it6505: Move a variable assignment behind a null pointer check in receive_timing_debugfs_show() Input: drv260x - sleep between polling GO bit drm/bridge: ti-sn65dsi83: Fix enable error path drm/bridge: tc358768: always enable HS video mode drm/bridge: tc358768: fix PLL parameters computation drm/bridge: tc358768: fix PLL target frequency drm/bridge: tc358768: fix TCLK_ZEROCNT computation drm/bridge: tc358768: Add atomic_get_input_bus_fmts() implementation drm/bridge: tc358768: fix TCLK_TRAILCNT computation drm/bridge: tc358768: fix THS_ZEROCNT computation drm/bridge: tc358768: fix TXTAGOCNT computation drm/bridge: tc358768: fix THS_TRAILCNT computation drm/vram-helper: fix function names in vram helper doc ARM: dts: BCM5301X: Drop "clock-names" from the SPI node ARM: dts: meson8b: correct uart_B and uart_C clock references mm: call arch_swap_restore() from do_swap_page() clk: vc5: Use `clamp()` to restrict PLL range bootmem: remove the vmemmap pages from kmemleak in free_bootmem_page clk: vc5: Fix .driver_data content in i2c_device_id clk: vc7: Fix .driver_data content in i2c_device_id clk: rs9: Fix .driver_data content in i2c_device_id Input: adxl34x - do not hardcode interrupt trigger type drm: sun4i_tcon: use devm_clk_get_enabled in `sun4i_tcon_init_clocks` drm/panel: sharp-ls043t1le01: adjust mode settings driver: soc: xilinx: use _safe loop iterator to avoid a use after free ASoC: Intel: sof_sdw: remove SOF_SDW_TGL_HDMI for MeteorLake devices drm/vkms: isolate pixel conversion functionality drm: Add fixed-point helper to get rounded integer values drm/vkms: Fix RGB565 pixel conversion ARM: dts: stm32: Move ethernet MAC EEPROM from SoM to carrier boards bus: ti-sysc: Fix dispc quirk masking bool variables arm64: dts: microchip: sparx5: do not use PSCI on reference boards drm/bridge: tc358767: Switch to devm MIPI-DSI helpers clk: imx: scu: use _safe list iterator to avoid a use after free hwmon: (f71882fg) prevent possible division by zero RDMA/bnxt_re: Disable/kill tasklet only if it is enabled RDMA/bnxt_re: Fix to remove unnecessary return labels RDMA/bnxt_re: Use unique names while registering interrupts RDMA/bnxt_re: Remove a redundant check inside bnxt_re_update_gid RDMA/bnxt_re: Fix to remove an unnecessary log drm/msm/dsi: don't allow enabling 14nm VCO with unprogrammed rate drm/msm/disp/dpu: get timing engine status from intf status register drm/msm/dpu: Set DPU_DATA_HCTL_EN for in INTF_SC7180_MASK iommu/virtio: Detach domain on endpoint release iommu/virtio: Return size mapped for a detached domain clk: renesas: rzg2l: Fix CPG_SIPLL5_CLK1 register write ARM: dts: gta04: Move model property out of pinctrl node drm/bridge: anx7625: Convert to i2c's .probe_new() drm/bridge: anx7625: Prevent endless probe loop ARM: dts: qcom: msm8974: do not use underscore in node name (again) arm64: dts: qcom: msm8916: correct camss unit address arm64: dts: qcom: msm8916: correct MMC unit address arm64: dts: qcom: msm8994: correct SPMI unit address arm64: dts: qcom: msm8996: correct camss unit address arm64: dts: qcom: sdm630: correct camss unit address arm64: dts: qcom: sdm845: correct camss unit address arm64: dts: qcom: sm8350: Add GPI DMA compatible fallback arm64: dts: qcom: sm8350: correct DMA controller unit address arm64: dts: qcom: sdm845-polaris: add missing touchscreen child node reg arm64: dts: qcom: apq8016-sbc: Fix regulator constraints arm64: dts: qcom: apq8016-sbc: Fix 1.8V power rail on LS expansion drm/bridge: Introduce pre_enable_prev_first to alter bridge init order drm/bridge: ti-sn65dsi83: Fix enable/disable flow to meet spec drm/panel: simple: fix active size for Ampire AM-480272H3TMQW-T01H ARM: ep93xx: fix missing-prototype warnings ARM: omap2: fix missing tick_broadcast() prototype arm64: dts: qcom: pm7250b: add missing spmi-vadc include arm64: dts: qcom: apq8096: fix fixed regulator name property arm64: dts: mediatek: mt8183: Add mediatek,broken-save-restore-fw to kukui ARM: dts: stm32: Shorten the AV96 HDMI sound card name memory: brcmstb_dpfe: fix testing array offset after use ARM: dts: qcom: apq8074-dragonboard: Set DMA as remotely controlled ASoC: es8316: Increment max value for ALC Capture Target Volume control ASoC: es8316: Do not set rate constraints for unsupported MCLKs ARM: dts: meson8: correct uart_B and uart_C clock references soc/fsl/qe: fix usb.c build errors RDMA/irdma: avoid fortify-string warning in irdma_clr_wqes IB/hfi1: Fix wrong mmu_node used for user SDMA packet after invalidate RDMA/hns: Fix hns_roce_table_get return value ARM: dts: iwg20d-q7-common: Fix backlight pwm specifier arm64: dts: renesas: ulcb-kf: Remove flow control for SCIF1 drm/msm/dpu: set DSC flush bit correctly at MDP CTL flush register fbdev: omapfb: lcd_mipid: Fix an error handling path in mipid_spi_probe() arm64: dts: ti: k3-j7200: Fix physical address of pin Input: pm8941-powerkey - fix debounce on gen2+ PMICs ARM: dts: stm32: Fix audio routing on STM32MP15xx DHCOM PDK2 ARM: dts: stm32: fix i2s endpoint format property for stm32mp15xx-dkx hwmon: (gsc-hwmon) fix fan pwm temperature scaling hwmon: (pmbus/adm1275) Fix problems with temperature monitoring on ADM1272 ARM: dts: BCM5301X: fix duplex-full => full-duplex clk: Export clk_hw_forward_rate_request() drm/amd/display: Fix a test CalculatePrefetchSchedule() drm/amd/display: Fix a test dml32_rq_dlg_get_rq_reg() drm/amdkfd: Fix potential deallocation of previously deallocated memory. soc: mediatek: SVS: Fix MT8192 GPU node name drm/amd/display: Fix artifacting on eDP panels when engaging freesync video mode drm/radeon: fix possible division-by-zero errors HID: uclogic: Modular KUnit tests should not depend on KUNIT=y RDMA/rxe: Add ibdev_dbg macros for rxe RDMA/rxe: Replace pr_xxx by rxe_dbg_xxx in rxe_mw.c RDMA/rxe: Fix access checks in rxe_check_bind_mw amdgpu: validate offset_in_bo of drm_amdgpu_gem_va drm/msm/a5xx: really check for A510 in a5xx_gpu_init RDMA/bnxt_re: wraparound mbox producer index RDMA/bnxt_re: Avoid calling wake_up threads from spin_lock context clk: imx: clk-imxrt1050: fix memory leak in imxrt1050_clocks_probe clk: imx: clk-imx8mn: fix memory leak in imx8mn_clocks_probe clk: imx93: fix memory leak and missing unwind goto in imx93_clocks_probe clk: imx: clk-imx8mp: improve error handling in imx8mp_clocks_probe() arm64: dts: qcom: sdm845: Flush RSC sleep & wake votes arm64: dts: qcom: sm8250-edo: Panel framebuffer is 2.5k instead of 4k clk: bcm: rpi: Fix off by one in raspberrypi_discover_clocks() clk: clocking-wizard: Fix Oops in clk_wzrd_register_divider() clk: tegra: tegra124-emc: Fix potential memory leak ALSA: ac97: Fix possible NULL dereference in snd_ac97_mixer drm/msm/dpu: do not enable color-management if DSPPs are not available drm/msm/dpu: Fix slice_last_group_size calculation drm/msm/dsi: Use DSC slice(s) packet size to compute word count drm/msm/dsi: Flip greater-than check for slice_count and slice_per_intf drm/msm/dsi: Remove incorrect references to slice_count drm/msm/dp: Free resources after unregistering them arm64: dts: mediatek: Add cpufreq nodes for MT8192 arm64: dts: mediatek: mt8192: Fix CPUs capacity-dmips-mhz drm/amdgpu: Fix memcpy() in sienna_cichlid_append_powerplay_table function. drm/amdgpu: Fix usage of UMC fill record in RAS drm/msm/dpu: correct MERGE_3D length clk: vc5: check memory returned by kasprintf() clk: cdce925: check return value of kasprintf() clk: si5341: return error if one synth clock registration fails clk: si5341: check return value of {devm_}kasprintf() clk: si5341: free unused memory on probe failure clk: keystone: sci-clk: check return value of kasprintf() clk: ti: clkctrl: check return value of kasprintf() drivers: meson: secure-pwrc: always enable DMA domain ovl: update of dentry revalidate flags after copy up ASoC: imx-audmix: check return value of devm_kasprintf() clk: Fix memory leak in devm_clk_notifier_register() ARM: dts: lan966x: kontron-d10: fix board reset ARM: dts: lan966x: kontron-d10: fix SPI CS ASoC: amd: acp: clear pdm dma interrupt mask PCI: cadence: Fix Gen2 Link Retraining process PCI: vmd: Reset VMD config register between soft reboots scsi: qedf: Fix NULL dereference in error handling pinctrl: bcm2835: Handle gpiochip_add_pin_range() errors platform/x86: lenovo-yogabook: Fix work race on remove() platform/x86: lenovo-yogabook: Reprobe devices on remove() platform/x86: lenovo-yogabook: Set default keyboard backligh brightness on probe() PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free scsi: 3w-xxxx: Add error handling for initialization failure in tw_probe() PCI: pciehp: Cancel bringup sequence if card is not present PCI: ftpci100: Release the clock resources pinctrl: sunplus: Add check for kmalloc PCI: Add pci_clear_master() stub for non-CONFIG_PCI scsi: lpfc: Revise NPIV ELS unsol rcv cmpl logic to drop ndlp based on nlp_state perf bench: Add missing setlocale() call to allow usage of %'d style formatting pinctrl: cherryview: Return correct value if pin in push-pull mode platform/x86: think-lmi: mutex protection around multiple WMI calls platform/x86: think-lmi: Correct System password interface platform/x86: think-lmi: Correct NVME password handling pinctrl:sunplus: Add check for kmalloc pinctrl: npcm7xx: Add missing check for ioremap kcsan: Don't expect 64 bits atomic builtins from 32 bits architectures powerpc/interrupt: Don't read MSR from interrupt_exit_kernel_prepare() powerpc/signal32: Force inlining of __unsafe_save_user_regs() and save_tm_user_regs_unsafe() perf script: Fix allocation of evsel->priv related to per-event dump files platform/x86: thinkpad_acpi: Fix lkp-tests warnings for platform profiles perf dwarf-aux: Fix off-by-one in die_get_varname() platform/x86/dell/dell-rbtn: Fix resources leaking on error path perf tool x86: Consolidate is_amd check into single function perf tool x86: Fix perf_env memory leak powerpc/64s: Fix VAS mm use after free pinctrl: microchip-sgpio: check return value of devm_kasprintf() pinctrl: at91-pio4: check return value of devm_kasprintf() powerpc/powernv/sriov: perform null check on iov before dereferencing iov powerpc: simplify ppc_save_regs powerpc: update ppc_save_regs to save current r1 in pt_regs PCI: qcom: Remove PCIE20_ prefix from register definitions PCI: qcom: Sort and group registers and bitfield definitions PCI: qcom: Use lower case for hex PCI: qcom: Use DWC helpers for modifying the read-only DBI registers PCI: qcom: Disable write access to read only registers for IP v2.9.0 riscv: uprobes: Restore thread.bad_cause powerpc/book3s64/mm: Fix DirectMap stats in /proc/meminfo powerpc/mm/dax: Fix the condition when checking if altmap vmemap can cross-boundary PCI: endpoint: Fix Kconfig indent style PCI: endpoint: Fix a Kconfig prompt of vNTB driver PCI: endpoint: functions/pci-epf-test: Fix dma_chan direction PCI: vmd: Fix uninitialized variable usage in vmd_enable_domain() vfio/mdev: Move the compat_class initialization to module init hwrng: virtio - Fix race on data_avail and actual data modpost: remove broken calculation of exception_table_entry size crypto: nx - fix build warnings when DEBUG_FS is not enabled modpost: fix section mismatch message for R_ARM_ABS32 modpost: fix section mismatch message for R_ARM_{PC24,CALL,JUMP24} crypto: marvell/cesa - Fix type mismatch warning crypto: jitter - correct health test during initialization modpost: fix off by one in is_executable_section() ARC: define ASM_NL and __ALIGN(_STR) outside #ifdef __ASSEMBLY__ guard crypto: kpp - Add helper to set reqsize crypto: qat - Use helper to set reqsize crypto: qat - unmap buffer before free for DH crypto: qat - unmap buffers before free for RSA NFSv4.2: fix wrong shrinker_id NFSv4.1: freeze the session table upon receiving NFS4ERR_BADSESSION SMB3: Do not send lease break acknowledgment if all file handles have been closed dax: Fix dax_mapping_release() use after free dax: Introduce alloc_dev_dax_id() dax/kmem: Pass valid argument to memory_group_register_static hwrng: st - keep clock enabled while hwrng is registered kbuild: Disable GCOV for *.mod.o efi/libstub: Disable PCI DMA before grabbing the EFI memory map cifs: prevent use-after-free by freeing the cfile later cifs: do all necessary checks for credits within or before locking smb: client: fix broken file attrs with nodfs mounts ksmbd: avoid field overflow warning arm64: sme: Use STR P to clear FFR context field in streaming SVE mode x86/efi: Make efi_set_virtual_address_map IBT safe md/raid1-10: fix casting from randomized structure in raid1_submit_write() USB: serial: option: add LARA-R6 01B PIDs usb: dwc3: gadget: Propagate core init errors to UDC during pullup phy: tegra: xusb: Clear the driver reference in usb-phy dev iio: adc: ad7192: Fix null ad7192_state pointer access iio: adc: ad7192: Fix internal/external clock selection iio: accel: fxls8962af: errata bug only applicable for FXLS8962AF iio: accel: fxls8962af: fixup buffer scan element type Revert "drm/amd/display: edp do not add non-edid timings" mm/mmap: Fix VM_LOCKED check in do_vmi_align_munmap() ALSA: hda/realtek: Enable mute/micmute LEDs and limit mic boost on EliteBook ALSA: hda/realtek: Add quirk for Clevo NPx0SNx ALSA: jack: Fix mutex call in snd_jack_report() ALSA: pcm: Fix potential data race at PCM memory allocation helpers block: fix signed int overflow in Amiga partition support block: add overflow checks for Amiga partition support block: change all __u32 annotations to __be32 in affs_hardblocks.h block: increment diskseq on all media change events btrfs: fix race when deleting free space root from the dirty cow roots list SUNRPC: Fix UAF in svc_tcp_listen_data_ready() w1: w1_therm: fix locking behavior in convert_t w1: fix loop in w1_fini() dt-bindings: power: reset: qcom-pon: Only allow reboot-mode pre-pmk8350 f2fs: do not allow to defragment files have FI_COMPRESS_RELEASED sh: j2: Use ioremap() to translate device tree address into kernel memory usb: dwc2: platform: Improve error reporting for problems during .remove() usb: dwc2: Fix some error handling paths serial: 8250: omap: Fix freeing of resources on failed register clk: qcom: mmcc-msm8974: remove oxili_ocmemgx_clk clk: qcom: camcc-sc7180: Add parent dependency to all camera GDSCs clk: qcom: gcc-ipq6018: Use floor ops for sdcc clocks clk: qcom: gcc-qcm2290: Mark RCGs shared where applicable media: usb: Check az6007_read() return value media: amphion: drop repeated codec data for vc1l format media: amphion: drop repeated codec data for vc1g format media: amphion: initiate a drain of the capture queue in dynamic resolution change media: videodev2.h: Fix struct v4l2_input tuner index comment media: usb: siano: Fix warning due to null work_func_t function pointer media: i2c: Correct format propagation for st-mipid02 media: hi846: fix usage of pm_runtime_get_if_in_use() media: mediatek: vcodec: using decoder status instead of core work count clk: qcom: reset: support resetting multiple bits clk: qcom: ipq6018: fix networking resets clk: qcom: dispcc-qcm2290: Fix BI_TCXO_AO handling clk: qcom: dispcc-qcm2290: Fix GPLL0_OUT_DIV handling clk: qcom: mmcc-msm8974: use clk_rcg2_shared_ops for mdp_clk_src clock staging: vchiq_arm: mark vchiq_platform_init() static usb: dwc3: qcom: Fix potential memory leak usb: gadget: u_serial: Add null pointer check in gserial_suspend extcon: Fix kernel doc of property fields to avoid warnings extcon: Fix kernel doc of property capability fields to avoid warnings usb: phy: phy-tahvo: fix memory leak in tahvo_usb_probe() usb: hide unused usbfs_notify_suspend/resume functions usb: misc: eud: Fix eud sysfs path (use 'qcom_eud') serial: core: lock port for stop_rx() in uart_suspend_port() serial: 8250: lock port for stop_rx() in omap8250_irq() serial: core: lock port for start_rx() in uart_resume_port() serial: 8250: lock port for UART_IER access in omap8250_irq() kernfs: fix missing kernfs_idr_lock to remove an ID from the IDR lkdtm: replace ll_rw_block with submit_bh i3c: master: svc: fix cpu schedule in spin lock coresight: Fix loss of connection info when a module is unloaded mfd: rt5033: Drop rt5033-battery sub-device media: venus: helpers: Fix ALIGN() of non power of two media: atomisp: gmin_platform: fix out_len in gmin_get_config_dsm_var() sh: Avoid using IRQ0 on SH3 and SH4 gfs2: Fix duplicate should_fault_in_pages() call f2fs: fix potential deadlock due to unpaired node_write lock use f2fs: fix to avoid NULL pointer dereference f2fs_write_end_io() KVM: s390: fix KVM_S390_GET_CMMA_BITS for GFNs in memslot holes usb: dwc3: qcom: Release the correct resources in dwc3_qcom_remove() usb: dwc3: qcom: Fix an error handling path in dwc3_qcom_probe() usb: common: usb-conn-gpio: Set last role to unknown before initial detection usb: dwc3-meson-g12a: Fix an error handling path in dwc3_meson_g12a_probe() mfd: wcd934x: Fix an error handling path in wcd934x_slim_probe() mfd: intel-lpss: Add missing check for platform_get_resource Revert "usb: common: usb-conn-gpio: Set last role to unknown before initial detection" serial: 8250_omap: Use force_suspend and resume for system suspend device property: Fix documentation for fwnode_get_next_parent() device property: Clarify description of returned value in some functions drivers: fwnode: fix fwnode_irq_get[_byname]() nvmem: sunplus-ocotp: release otp->clk before return nvmem: rmem: Use NVMEM_DEVID_AUTO bus: fsl-mc: don't assume child devices are all fsl-mc devices mfd: stmfx: Fix error path in stmfx_chip_init mfd: stmfx: Nullify stmfx->vdd in case of error KVM: s390: vsie: fix the length of APCB bitmap KVM: s390/diag: fix racy access of physical cpu number in diag 9c handler cpufreq: mediatek: correct voltages for MT7622 and MT7623 misc: fastrpc: check return value of devm_kasprintf() clk: qcom: mmcc-msm8974: fix MDSS_GDSC power flags hwtracing: hisi_ptt: Fix potential sleep in atomic context mfd: stmpe: Only disable the regulators if they are enabled phy: tegra: xusb: check return value of devm_kzalloc() lib/bitmap: drop optimization of bitmap_{from,to}_arr64 pwm: imx-tpm: force 'real_period' to be zero in suspend pwm: sysfs: Do not apply state to already disabled PWMs pwm: ab8500: Fix error code in probe() pwm: mtk_disp: Fix the disable flow of disp_pwm md/raid10: fix the condition to call bio_end_io_acct() rtc: st-lpc: Release some resources in st_rtc_probe() in case of error drm/i915/psr: Use hw.adjusted mode when calculating io/fast wake times drm/i915/guc/slpc: Apply min softlimit correctly f2fs: check return value of freeze_super() media: cec: i2c: ch7322: also select REGMAP sctp: fix potential deadlock on &net->sctp.addr_wq_lock net/sched: act_ipt: add sanity checks on table name and hook locations net: add a couple of helpers for iph tot_len net/sched: act_ipt: add sanity checks on skb before calling target spi: spi-geni-qcom: enable SPI_CONTROLLER_MUST_TX for GPI DMA mode net: mscc: ocelot: don't report that RX timestamping is enabled by default net: mscc: ocelot: don't keep PTP configuration of all ports in single structure net: dsa: felix: don't drop PTP frames with tag_8021q when RX timestamping is disabled net: dsa: sja1105: always enable the INCL_SRCPT option net: dsa: tag_sja1105: always prefer source port information from INCL_SRCPT Add MODULE_FIRMWARE() for FIRMWARE_TG357766. Bluetooth: fix invalid-bdaddr quirk for non-persistent setup Bluetooth: ISO: use hci_sync for setting CIG parameters Bluetooth: MGMT: add CIS feature bits to controller information Bluetooth: MGMT: Use BIT macro when defining bitfields Bluetooth: MGMT: Fix marking SCAN_RSP as not connectable ibmvnic: Do not reset dql stats on NON_FATAL err net: dsa: vsc73xx: fix MTU configuration mlxsw: minimal: fix potential memory leak in mlxsw_m_linecards_init spi: bcm-qspi: return error if neither hif_mspi nor mspi is available drm/amdgpu: fix number of fence calculations drm/amd: Don't try to enable secure display TA multiple times mailbox: ti-msgmgr: Fill non-message tx data fields with 0x0 f2fs: fix error path handling in truncate_dnode() octeontx2-af: Fix mapping for NIX block from CGX connection octeontx2-af: Add validation before accessing cgx and lmac ntfs: Fix panic about slab-out-of-bounds caused by ntfs_listxattr() powerpc: allow PPC_EARLY_DEBUG_CPM only when SERIAL_CPM=y powerpc: dts: turris1x.dts: Fix PCIe MEM size for pci2 node net: bridge: keep ports without IFF_UNICAST_FLT in BR_PROMISC mode net: dsa: tag_sja1105: fix source port decoding in vlan_filtering=0 bridge mode net: fix net_dev_start_xmit trace event vs skb_transport_offset() tcp: annotate data races in __tcp_oow_rate_limited() bpf, btf: Warn but return no error for NULL btf from __register_btf_kfunc_id_set() xsk: Honor SO_BINDTODEVICE on bind net/sched: act_pedit: Add size check for TCA_PEDIT_PARMS_EX fanotify: disallow mount/sb marks on kernel internal pseudo fs riscv: move memblock_allow_resize() after linear mapping is ready pptp: Fix fib lookup calls. net: dsa: tag_sja1105: fix MAC DA patching from meta frames net: dsa: sja1105: always enable the send_meta options octeontx-af: fix hardware timestamp configuration afs: Fix accidental truncation when storing data s390/qeth: Fix vipa deletion sh: dma: Fix DMA channel offset calculation apparmor: fix missing error check for rhashtable_insert_fast i2c: xiic: Don't try to handle more interrupt events after error dm: fix undue/missing spaces dm: avoid split of quoted strings where possible dm ioctl: have constant on the right side of the test dm ioctl: Avoid double-fetch of version extcon: usbc-tusb320: Convert to i2c's .probe_new() extcon: usbc-tusb320: Unregister typec port on driver removal btrfs: do not BUG_ON() on tree mod log failure at balance_level() i2c: qup: Add missing unwind goto in qup_i2c_probe() irqchip/loongson-pch-pic: Fix potential incorrect hwirq assignment NFSD: add encoding of op_recall flag for write delegation irqchip/loongson-pch-pic: Fix initialization of HT vector register io_uring: wait interruptibly for request completions on exit mmc: core: disable TRIM on Kingston EMMC04G-M627 mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M mmc: mmci: Set PROBE_PREFER_ASYNCHRONOUS mmc: sdhci: fix DMA configure compatibility issue when 64bit DMA mode is used. wifi: cfg80211: fix regulatory disconnect for non-MLO wifi: ath10k: Serialize wake_tx_queue ops wifi: mt76: mt7921e: fix init command fail with enabled device bcache: fixup btree_cache_wait list damage bcache: Remove unnecessary NULL point check in node allocations bcache: Fix __bch_btree_node_alloc to make the failure behavior consistent watch_queue: prevent dangling pipe pointer um: Use HOST_DIR for mrproper integrity: Fix possible multiple allocation in integrity_inode_get() autofs: use flexible array in ioctl structure mm/damon/ops-common: atomically test and clear young on ptes and pmds shmem: use ramfs_kill_sb() for kill_sb method of ramfs-based tmpfs jffs2: reduce stack usage in jffs2_build_xattr_subsystem() fs: avoid empty option when generating legacy mount string ext4: Remove ext4 locking of moved directory Revert "f2fs: fix potential corruption when moving a directory" fs: Establish locking order for unrelated directories fs: Lock moved directories i2c: nvidia-gpu: Add ACPI property to align with device-tree i2c: nvidia-gpu: Remove ccgx,firmware-build property usb: typec: ucsi: Mark dGPUs as DEVICE scope ipvs: increase ip_vs_conn_tab_bits range for 64BIT btrfs: add handling for RAID1C23/DUP to btrfs_reduce_alloc_profile btrfs: delete unused BGs while reclaiming BGs btrfs: bail out reclaim process if filesystem is read-only btrfs: add block-group tree to lockdep classes btrfs: reinsert BGs failed to reclaim btrfs: fix race when deleting quota root from the dirty cow roots list btrfs: fix extent buffer leak after tree mod log failure at split_node() btrfs: do not BUG_ON() on tree mod log failure at __btrfs_cow_block() ASoC: mediatek: mt8173: Fix irq error path ASoC: mediatek: mt8173: Fix snd_soc_component_initialize error path regulator: tps65219: Fix matching interrupts for their regulators ARM: dts: qcom: ipq4019: fix broken NAND controller properties override ARM: orion5x: fix d2net gpio initialization leds: trigger: netdev: Recheck NETDEV_LED_MODE_LINKUP on dev rename blktrace: use inline function for blk_trace_remove() while blktrace is disabled fs: no need to check source xfs: explicitly specify cpu when forcing inodegc delayed work to run immediately xfs: check that per-cpu inodegc workers actually run on that cpu xfs: disable reaping in fscounters scrub xfs: fix xfs_inodegc_stop racing with mod_delayed_work mm/mmap: Fix extra maple tree write drm/i915: Fix TypeC mode initialization during system resume drm/i915/tc: Fix TC port link ref init for DP MST during HW readout drm/i915/tc: Fix system resume MST mode restore for DP-alt sinks mtd: parsers: refer to ARCH_BCMBCA instead of ARCH_BCM4908 netfilter: nf_tables: unbind non-anonymous set if rule construction fails netfilter: conntrack: Avoid nf_ct_helper_hash uses after free netfilter: nf_tables: do not ignore genmask when looking up chain by id netfilter: nf_tables: prevent OOB access in nft_byteorder_eval wireguard: queueing: use saner cpu selection wrapping wireguard: netlink: send staged packets when setting initial private key tty: serial: fsl_lpuart: add earlycon for imx8ulp platform block/partition: fix signedness issue for Amiga partitions sh: mach-r2d: Handle virq offset in cascaded IRL demux sh: mach-highlander: Handle virq offset in cascaded IRL demux sh: mach-dreamcast: Handle virq offset in cascaded IRQ demux sh: hd64461: Handle virq offset for offchip IRQ base and HD64461 IRQ io_uring: Use io_schedule* in cqring wait Linux 6.1.39 Change-Id: I5867c943c99c157fa599ecd08da961c632e58302 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
1335 lines
31 KiB
C
1335 lines
31 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
*
|
|
* Copyright (C) 2011 Novell Inc.
|
|
*/
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/xattr.h>
|
|
#include <linux/security.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/module.h>
|
|
#include <linux/posix_acl.h>
|
|
#include <linux/posix_acl_xattr.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/ratelimit.h>
|
|
#include "overlayfs.h"
|
|
|
|
static unsigned short ovl_redirect_max = 256;
|
|
module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);
|
|
MODULE_PARM_DESC(redirect_max,
|
|
"Maximum length of absolute redirect xattr value");
|
|
|
|
static int ovl_set_redirect(struct dentry *dentry, bool samedir);
|
|
|
|
int ovl_cleanup(struct ovl_fs *ofs, struct inode *wdir, struct dentry *wdentry)
|
|
{
|
|
int err;
|
|
|
|
dget(wdentry);
|
|
if (d_is_dir(wdentry))
|
|
err = ovl_do_rmdir(ofs, wdir, wdentry);
|
|
else
|
|
err = ovl_do_unlink(ofs, wdir, wdentry);
|
|
dput(wdentry);
|
|
|
|
if (err) {
|
|
pr_err("cleanup of '%pd2' failed (%i)\n",
|
|
wdentry, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir)
|
|
{
|
|
struct dentry *temp;
|
|
char name[20];
|
|
static atomic_t temp_id = ATOMIC_INIT(0);
|
|
|
|
/* counter is allowed to wrap, since temp dentries are ephemeral */
|
|
snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
|
|
|
|
temp = ovl_lookup_upper(ofs, name, workdir, strlen(name));
|
|
if (!IS_ERR(temp) && temp->d_inode) {
|
|
pr_err("workdir/%s already exists\n", name);
|
|
dput(temp);
|
|
temp = ERR_PTR(-EIO);
|
|
}
|
|
|
|
return temp;
|
|
}
|
|
|
|
/* caller holds i_mutex on workdir */
|
|
static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
|
|
{
|
|
int err;
|
|
struct dentry *whiteout;
|
|
struct dentry *workdir = ofs->workdir;
|
|
struct inode *wdir = workdir->d_inode;
|
|
|
|
if (!ofs->whiteout) {
|
|
whiteout = ovl_lookup_temp(ofs, workdir);
|
|
if (IS_ERR(whiteout))
|
|
goto out;
|
|
|
|
err = ovl_do_whiteout(ofs, wdir, whiteout);
|
|
if (err) {
|
|
dput(whiteout);
|
|
whiteout = ERR_PTR(err);
|
|
goto out;
|
|
}
|
|
ofs->whiteout = whiteout;
|
|
}
|
|
|
|
if (ofs->share_whiteout) {
|
|
whiteout = ovl_lookup_temp(ofs, workdir);
|
|
if (IS_ERR(whiteout))
|
|
goto out;
|
|
|
|
err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout);
|
|
if (!err)
|
|
goto out;
|
|
|
|
if (err != -EMLINK) {
|
|
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n",
|
|
ofs->whiteout->d_inode->i_nlink, err);
|
|
ofs->share_whiteout = false;
|
|
}
|
|
dput(whiteout);
|
|
}
|
|
whiteout = ofs->whiteout;
|
|
ofs->whiteout = NULL;
|
|
out:
|
|
return whiteout;
|
|
}
|
|
|
|
/* Caller must hold i_mutex on both workdir and dir */
|
|
int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
|
|
struct dentry *dentry)
|
|
{
|
|
struct inode *wdir = ofs->workdir->d_inode;
|
|
struct dentry *whiteout;
|
|
int err;
|
|
int flags = 0;
|
|
|
|
whiteout = ovl_whiteout(ofs);
|
|
err = PTR_ERR(whiteout);
|
|
if (IS_ERR(whiteout))
|
|
return err;
|
|
|
|
if (d_is_dir(dentry))
|
|
flags = RENAME_EXCHANGE;
|
|
|
|
err = ovl_do_rename(ofs, wdir, whiteout, dir, dentry, flags);
|
|
if (err)
|
|
goto kill_whiteout;
|
|
if (flags)
|
|
ovl_cleanup(ofs, wdir, dentry);
|
|
|
|
out:
|
|
dput(whiteout);
|
|
return err;
|
|
|
|
kill_whiteout:
|
|
ovl_cleanup(ofs, wdir, whiteout);
|
|
goto out;
|
|
}
|
|
|
|
int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
|
|
struct dentry **newdentry, umode_t mode)
|
|
{
|
|
int err;
|
|
struct dentry *d, *dentry = *newdentry;
|
|
|
|
err = ovl_do_mkdir(ofs, dir, dentry, mode);
|
|
if (err)
|
|
return err;
|
|
|
|
if (likely(!d_unhashed(dentry)))
|
|
return 0;
|
|
|
|
/*
|
|
* vfs_mkdir() may succeed and leave the dentry passed
|
|
* to it unhashed and negative. If that happens, try to
|
|
* lookup a new hashed and positive dentry.
|
|
*/
|
|
d = ovl_lookup_upper(ofs, dentry->d_name.name, dentry->d_parent,
|
|
dentry->d_name.len);
|
|
if (IS_ERR(d)) {
|
|
pr_warn("failed lookup after mkdir (%pd2, err=%i).\n",
|
|
dentry, err);
|
|
return PTR_ERR(d);
|
|
}
|
|
dput(dentry);
|
|
*newdentry = d;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
|
|
struct dentry *newdentry, struct ovl_cattr *attr)
|
|
{
|
|
int err;
|
|
|
|
if (IS_ERR(newdentry))
|
|
return newdentry;
|
|
|
|
err = -ESTALE;
|
|
if (newdentry->d_inode)
|
|
goto out;
|
|
|
|
if (attr->hardlink) {
|
|
err = ovl_do_link(ofs, attr->hardlink, dir, newdentry);
|
|
} else {
|
|
switch (attr->mode & S_IFMT) {
|
|
case S_IFREG:
|
|
err = ovl_do_create(ofs, dir, newdentry, attr->mode);
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
/* mkdir is special... */
|
|
err = ovl_mkdir_real(ofs, dir, &newdentry, attr->mode);
|
|
break;
|
|
|
|
case S_IFCHR:
|
|
case S_IFBLK:
|
|
case S_IFIFO:
|
|
case S_IFSOCK:
|
|
err = ovl_do_mknod(ofs, dir, newdentry, attr->mode,
|
|
attr->rdev);
|
|
break;
|
|
|
|
case S_IFLNK:
|
|
err = ovl_do_symlink(ofs, dir, newdentry, attr->link);
|
|
break;
|
|
|
|
default:
|
|
err = -EPERM;
|
|
}
|
|
}
|
|
if (!err && WARN_ON(!newdentry->d_inode)) {
|
|
/*
|
|
* Not quite sure if non-instantiated dentry is legal or not.
|
|
* VFS doesn't seem to care so check and warn here.
|
|
*/
|
|
err = -EIO;
|
|
}
|
|
out:
|
|
if (err) {
|
|
dput(newdentry);
|
|
return ERR_PTR(err);
|
|
}
|
|
return newdentry;
|
|
}
|
|
|
|
struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
|
|
struct ovl_cattr *attr)
|
|
{
|
|
return ovl_create_real(ofs, d_inode(workdir),
|
|
ovl_lookup_temp(ofs, workdir), attr);
|
|
}
|
|
|
|
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
|
|
int xerr)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
int err;
|
|
|
|
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
|
|
if (!err)
|
|
ovl_dentry_set_opaque(dentry);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
|
|
{
|
|
/*
|
|
* Fail with -EIO when trying to create opaque dir and upper doesn't
|
|
* support xattrs. ovl_rename() calls ovl_set_opaque_xerr(-EXDEV) to
|
|
* return a specific error for noxattr case.
|
|
*/
|
|
return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
|
|
}
|
|
|
|
/*
|
|
* Common operations required to be done after creation of file on upper.
|
|
* If @hardlink is false, then @inode is a pre-allocated inode, we may or
|
|
* may not use to instantiate the new dentry.
|
|
*/
|
|
static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
|
|
struct dentry *newdentry, bool hardlink)
|
|
{
|
|
struct ovl_inode_params oip = {
|
|
.upperdentry = newdentry,
|
|
.newinode = inode,
|
|
};
|
|
|
|
ovl_dir_modified(dentry->d_parent, false);
|
|
ovl_dentry_set_upper_alias(dentry);
|
|
ovl_dentry_init_reval(dentry, newdentry);
|
|
|
|
if (!hardlink) {
|
|
/*
|
|
* ovl_obtain_alias() can be called after ovl_create_real()
|
|
* and before we get here, so we may get an inode from cache
|
|
* with the same real upperdentry that is not the inode we
|
|
* pre-allocated. In this case we will use the cached inode
|
|
* to instantiate the new dentry.
|
|
*
|
|
* XXX: if we ever use ovl_obtain_alias() to decode directory
|
|
* file handles, need to use ovl_get_inode_locked() and
|
|
* d_instantiate_new() here to prevent from creating two
|
|
* hashed directory inode aliases.
|
|
*/
|
|
inode = ovl_get_inode(dentry->d_sb, &oip);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
if (inode == oip.newinode)
|
|
ovl_set_flag(OVL_UPPERDATA, inode);
|
|
} else {
|
|
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
|
|
dput(newdentry);
|
|
inc_nlink(inode);
|
|
}
|
|
|
|
d_instantiate(dentry, inode);
|
|
if (inode != oip.newinode) {
|
|
pr_warn_ratelimited("newly created inode found in cache (%pd2)\n",
|
|
dentry);
|
|
}
|
|
|
|
/* Force lookup of new upper hardlink to find its lower */
|
|
if (hardlink)
|
|
d_drop(dentry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool ovl_type_merge(struct dentry *dentry)
|
|
{
|
|
return OVL_TYPE_MERGE(ovl_path_type(dentry));
|
|
}
|
|
|
|
static bool ovl_type_origin(struct dentry *dentry)
|
|
{
|
|
return OVL_TYPE_ORIGIN(ovl_path_type(dentry));
|
|
}
|
|
|
|
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
|
struct ovl_cattr *attr)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
|
struct inode *udir = upperdir->d_inode;
|
|
struct dentry *newdentry;
|
|
int err;
|
|
|
|
if (!attr->hardlink && !IS_POSIXACL(udir))
|
|
attr->mode &= ~current_umask();
|
|
|
|
inode_lock_nested(udir, I_MUTEX_PARENT);
|
|
newdentry = ovl_create_real(ofs, udir,
|
|
ovl_lookup_upper(ofs, dentry->d_name.name,
|
|
upperdir, dentry->d_name.len),
|
|
attr);
|
|
err = PTR_ERR(newdentry);
|
|
if (IS_ERR(newdentry))
|
|
goto out_unlock;
|
|
|
|
if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) &&
|
|
!ovl_allow_offline_changes(ofs)) {
|
|
/* Setting opaque here is just an optimization, allow to fail */
|
|
ovl_set_opaque(dentry, newdentry);
|
|
}
|
|
|
|
err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
|
|
if (err)
|
|
goto out_cleanup;
|
|
out_unlock:
|
|
inode_unlock(udir);
|
|
return err;
|
|
|
|
out_cleanup:
|
|
ovl_cleanup(ofs, udir, newdentry);
|
|
dput(newdentry);
|
|
goto out_unlock;
|
|
}
|
|
|
|
static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|
struct list_head *list)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
struct dentry *workdir = ovl_workdir(dentry);
|
|
struct inode *wdir = workdir->d_inode;
|
|
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
|
struct inode *udir = upperdir->d_inode;
|
|
struct path upperpath;
|
|
struct dentry *upper;
|
|
struct dentry *opaquedir;
|
|
struct kstat stat;
|
|
int err;
|
|
|
|
if (WARN_ON(!workdir))
|
|
return ERR_PTR(-EROFS);
|
|
|
|
err = ovl_lock_rename_workdir(workdir, upperdir);
|
|
if (err)
|
|
goto out;
|
|
|
|
ovl_path_upper(dentry, &upperpath);
|
|
err = vfs_getattr(&upperpath, &stat,
|
|
STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
|
|
if (err)
|
|
goto out_unlock;
|
|
|
|
err = -ESTALE;
|
|
if (!S_ISDIR(stat.mode))
|
|
goto out_unlock;
|
|
upper = upperpath.dentry;
|
|
if (upper->d_parent->d_inode != udir)
|
|
goto out_unlock;
|
|
|
|
opaquedir = ovl_create_temp(ofs, workdir, OVL_CATTR(stat.mode));
|
|
err = PTR_ERR(opaquedir);
|
|
if (IS_ERR(opaquedir))
|
|
goto out_unlock;
|
|
|
|
err = ovl_copy_xattr(dentry->d_sb, &upperpath, opaquedir);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
err = ovl_set_opaque(dentry, opaquedir);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
inode_lock(opaquedir->d_inode);
|
|
err = ovl_set_attr(ofs, opaquedir, &stat);
|
|
inode_unlock(opaquedir->d_inode);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
err = ovl_do_rename(ofs, wdir, opaquedir, udir, upper, RENAME_EXCHANGE);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
ovl_cleanup_whiteouts(ofs, upper, list);
|
|
ovl_cleanup(ofs, wdir, upper);
|
|
unlock_rename(workdir, upperdir);
|
|
|
|
/* dentry's upper doesn't match now, get rid of it */
|
|
d_drop(dentry);
|
|
|
|
return opaquedir;
|
|
|
|
out_cleanup:
|
|
ovl_cleanup(ofs, wdir, opaquedir);
|
|
dput(opaquedir);
|
|
out_unlock:
|
|
unlock_rename(workdir, upperdir);
|
|
out:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static int ovl_set_upper_acl(struct ovl_fs *ofs, struct dentry *upperdentry,
|
|
const char *name, const struct posix_acl *acl)
|
|
{
|
|
void *buffer;
|
|
size_t size;
|
|
int err;
|
|
|
|
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl)
|
|
return 0;
|
|
|
|
size = posix_acl_xattr_size(acl->a_count);
|
|
buffer = kmalloc(size, GFP_KERNEL);
|
|
if (!buffer)
|
|
return -ENOMEM;
|
|
|
|
err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
|
if (err < 0)
|
|
goto out_free;
|
|
|
|
err = ovl_do_setxattr(ofs, upperdentry, name, buffer, size, XATTR_CREATE);
|
|
out_free:
|
|
kfree(buffer);
|
|
return err;
|
|
}
|
|
|
|
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
|
struct ovl_cattr *cattr)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
struct dentry *workdir = ovl_workdir(dentry);
|
|
struct inode *wdir = workdir->d_inode;
|
|
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
|
struct inode *udir = upperdir->d_inode;
|
|
struct dentry *upper;
|
|
struct dentry *newdentry;
|
|
int err;
|
|
struct posix_acl *acl, *default_acl;
|
|
bool hardlink = !!cattr->hardlink;
|
|
|
|
if (WARN_ON(!workdir))
|
|
return -EROFS;
|
|
|
|
if (!hardlink) {
|
|
err = posix_acl_create(dentry->d_parent->d_inode,
|
|
&cattr->mode, &default_acl, &acl);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
err = ovl_lock_rename_workdir(workdir, upperdir);
|
|
if (err)
|
|
goto out;
|
|
|
|
upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
|
|
dentry->d_name.len);
|
|
err = PTR_ERR(upper);
|
|
if (IS_ERR(upper))
|
|
goto out_unlock;
|
|
|
|
err = -ESTALE;
|
|
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
|
|
goto out_dput;
|
|
|
|
newdentry = ovl_create_temp(ofs, workdir, cattr);
|
|
err = PTR_ERR(newdentry);
|
|
if (IS_ERR(newdentry))
|
|
goto out_dput;
|
|
|
|
/*
|
|
* mode could have been mutilated due to umask (e.g. sgid directory)
|
|
*/
|
|
if (!hardlink &&
|
|
!S_ISLNK(cattr->mode) &&
|
|
newdentry->d_inode->i_mode != cattr->mode) {
|
|
struct iattr attr = {
|
|
.ia_valid = ATTR_MODE,
|
|
.ia_mode = cattr->mode,
|
|
};
|
|
inode_lock(newdentry->d_inode);
|
|
err = ovl_do_notify_change(ofs, newdentry, &attr);
|
|
inode_unlock(newdentry->d_inode);
|
|
if (err)
|
|
goto out_cleanup;
|
|
}
|
|
if (!hardlink) {
|
|
err = ovl_set_upper_acl(ofs, newdentry,
|
|
XATTR_NAME_POSIX_ACL_ACCESS, acl);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
err = ovl_set_upper_acl(ofs, newdentry,
|
|
XATTR_NAME_POSIX_ACL_DEFAULT, default_acl);
|
|
if (err)
|
|
goto out_cleanup;
|
|
}
|
|
|
|
if (!hardlink && S_ISDIR(cattr->mode)) {
|
|
err = ovl_set_opaque(dentry, newdentry);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
err = ovl_do_rename(ofs, wdir, newdentry, udir, upper,
|
|
RENAME_EXCHANGE);
|
|
if (err)
|
|
goto out_cleanup;
|
|
|
|
ovl_cleanup(ofs, wdir, upper);
|
|
} else {
|
|
err = ovl_do_rename(ofs, wdir, newdentry, udir, upper, 0);
|
|
if (err)
|
|
goto out_cleanup;
|
|
}
|
|
err = ovl_instantiate(dentry, inode, newdentry, hardlink);
|
|
if (err) {
|
|
ovl_cleanup(ofs, udir, newdentry);
|
|
dput(newdentry);
|
|
}
|
|
out_dput:
|
|
dput(upper);
|
|
out_unlock:
|
|
unlock_rename(workdir, upperdir);
|
|
out:
|
|
if (!hardlink) {
|
|
posix_acl_release(acl);
|
|
posix_acl_release(default_acl);
|
|
}
|
|
return err;
|
|
|
|
out_cleanup:
|
|
ovl_cleanup(ofs, wdir, newdentry);
|
|
dput(newdentry);
|
|
goto out_dput;
|
|
}
|
|
|
|
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
|
|
struct ovl_cattr *attr, bool origin)
|
|
{
|
|
int err;
|
|
const struct cred *old_cred, *hold_cred = NULL;
|
|
struct cred *override_cred;
|
|
struct dentry *parent = dentry->d_parent;
|
|
|
|
err = ovl_copy_up(parent);
|
|
if (err)
|
|
return err;
|
|
|
|
old_cred = ovl_override_creds(dentry->d_sb);
|
|
|
|
/*
|
|
* When linking a file with copy up origin into a new parent, mark the
|
|
* new parent dir "impure".
|
|
*/
|
|
if (origin) {
|
|
err = ovl_set_impure(parent, ovl_dentry_upper(parent));
|
|
if (err)
|
|
goto out_revert_creds;
|
|
}
|
|
|
|
if (!attr->hardlink) {
|
|
err = -ENOMEM;
|
|
override_cred = prepare_creds();
|
|
if (!override_cred)
|
|
goto out_revert_creds;
|
|
/*
|
|
* In the creation cases(create, mkdir, mknod, symlink),
|
|
* ovl should transfer current's fs{u,g}id to underlying
|
|
* fs. Because underlying fs want to initialize its new
|
|
* inode owner using current's fs{u,g}id. And in this
|
|
* case, the @inode is a new inode that is initialized
|
|
* in inode_init_owner() to current's fs{u,g}id. So use
|
|
* the inode's i_{u,g}id to override the cred's fs{u,g}id.
|
|
*
|
|
* But in the other hardlink case, ovl_link() does not
|
|
* create a new inode, so just use the ovl mounter's
|
|
* fs{u,g}id.
|
|
*/
|
|
override_cred->fsuid = inode->i_uid;
|
|
override_cred->fsgid = inode->i_gid;
|
|
err = security_dentry_create_files_as(dentry,
|
|
attr->mode, &dentry->d_name,
|
|
old_cred ? old_cred : current_cred(),
|
|
override_cred);
|
|
if (err) {
|
|
put_cred(override_cred);
|
|
goto out_revert_creds;
|
|
}
|
|
hold_cred = override_creds(override_cred);
|
|
put_cred(override_cred);
|
|
}
|
|
|
|
if (!ovl_dentry_is_whiteout(dentry))
|
|
err = ovl_create_upper(dentry, inode, attr);
|
|
else
|
|
err = ovl_create_over_whiteout(dentry, inode, attr);
|
|
|
|
out_revert_creds:
|
|
ovl_revert_creds(dentry->d_sb, old_cred ?: hold_cred);
|
|
if (old_cred && hold_cred)
|
|
put_cred(hold_cred);
|
|
return err;
|
|
}
|
|
|
|
static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
|
|
const char *link)
|
|
{
|
|
int err;
|
|
struct inode *inode;
|
|
struct ovl_cattr attr = {
|
|
.rdev = rdev,
|
|
.link = link,
|
|
};
|
|
|
|
err = ovl_want_write(dentry);
|
|
if (err)
|
|
goto out;
|
|
|
|
/* Preallocate inode to be used by ovl_get_inode() */
|
|
err = -ENOMEM;
|
|
inode = ovl_new_inode(dentry->d_sb, mode, rdev);
|
|
if (!inode)
|
|
goto out_drop_write;
|
|
|
|
spin_lock(&inode->i_lock);
|
|
inode->i_state |= I_CREATING;
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
inode_init_owner(&init_user_ns, inode, dentry->d_parent->d_inode, mode);
|
|
attr.mode = inode->i_mode;
|
|
|
|
err = ovl_create_or_link(dentry, inode, &attr, false);
|
|
/* Did we end up using the preallocated inode? */
|
|
if (inode != d_inode(dentry))
|
|
iput(inode);
|
|
|
|
out_drop_write:
|
|
ovl_drop_write(dentry);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int ovl_create(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, bool excl)
|
|
{
|
|
return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL);
|
|
}
|
|
|
|
static int ovl_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode)
|
|
{
|
|
return ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL);
|
|
}
|
|
|
|
static int ovl_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, umode_t mode, dev_t rdev)
|
|
{
|
|
/* Don't allow creation of "whiteout" on overlay */
|
|
if (S_ISCHR(mode) && rdev == WHITEOUT_DEV)
|
|
return -EPERM;
|
|
|
|
return ovl_create_object(dentry, mode, rdev, NULL);
|
|
}
|
|
|
|
static int ovl_symlink(struct user_namespace *mnt_userns, struct inode *dir,
|
|
struct dentry *dentry, const char *link)
|
|
{
|
|
return ovl_create_object(dentry, S_IFLNK, 0, link);
|
|
}
|
|
|
|
static int ovl_set_link_redirect(struct dentry *dentry)
|
|
{
|
|
const struct cred *old_cred;
|
|
int err;
|
|
|
|
old_cred = ovl_override_creds(dentry->d_sb);
|
|
err = ovl_set_redirect(dentry, false);
|
|
ovl_revert_creds(dentry->d_sb, old_cred);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ovl_link(struct dentry *old, struct inode *newdir,
|
|
struct dentry *new)
|
|
{
|
|
int err;
|
|
struct inode *inode;
|
|
|
|
err = ovl_want_write(old);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = ovl_copy_up(old);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
err = ovl_copy_up(new->d_parent);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
if (ovl_is_metacopy_dentry(old)) {
|
|
err = ovl_set_link_redirect(old);
|
|
if (err)
|
|
goto out_drop_write;
|
|
}
|
|
|
|
err = ovl_nlink_start(old);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
inode = d_inode(old);
|
|
ihold(inode);
|
|
|
|
err = ovl_create_or_link(new, inode,
|
|
&(struct ovl_cattr) {.hardlink = ovl_dentry_upper(old)},
|
|
ovl_type_origin(old));
|
|
if (err)
|
|
iput(inode);
|
|
|
|
ovl_nlink_end(old);
|
|
out_drop_write:
|
|
ovl_drop_write(old);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper)
|
|
{
|
|
return d_inode(ovl_dentry_upper(dentry)) == d_inode(upper);
|
|
}
|
|
|
|
static int ovl_remove_and_whiteout(struct dentry *dentry,
|
|
struct list_head *list)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
struct dentry *workdir = ovl_workdir(dentry);
|
|
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
|
struct dentry *upper;
|
|
struct dentry *opaquedir = NULL;
|
|
int err;
|
|
|
|
if (WARN_ON(!workdir))
|
|
return -EROFS;
|
|
|
|
if (!list_empty(list)) {
|
|
opaquedir = ovl_clear_empty(dentry, list);
|
|
err = PTR_ERR(opaquedir);
|
|
if (IS_ERR(opaquedir))
|
|
goto out;
|
|
}
|
|
|
|
err = ovl_lock_rename_workdir(workdir, upperdir);
|
|
if (err)
|
|
goto out_dput;
|
|
|
|
upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
|
|
dentry->d_name.len);
|
|
err = PTR_ERR(upper);
|
|
if (IS_ERR(upper))
|
|
goto out_unlock;
|
|
|
|
err = -ESTALE;
|
|
if ((opaquedir && upper != opaquedir) ||
|
|
(!opaquedir && ovl_dentry_upper(dentry) &&
|
|
!ovl_matches_upper(dentry, upper))) {
|
|
goto out_dput_upper;
|
|
}
|
|
|
|
err = ovl_cleanup_and_whiteout(ofs, d_inode(upperdir), upper);
|
|
if (err)
|
|
goto out_d_drop;
|
|
|
|
ovl_dir_modified(dentry->d_parent, true);
|
|
out_d_drop:
|
|
d_drop(dentry);
|
|
out_dput_upper:
|
|
dput(upper);
|
|
out_unlock:
|
|
unlock_rename(workdir, upperdir);
|
|
out_dput:
|
|
dput(opaquedir);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
|
|
struct list_head *list)
|
|
{
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
|
|
struct inode *dir = upperdir->d_inode;
|
|
struct dentry *upper;
|
|
struct dentry *opaquedir = NULL;
|
|
int err;
|
|
|
|
if (!list_empty(list)) {
|
|
opaquedir = ovl_clear_empty(dentry, list);
|
|
err = PTR_ERR(opaquedir);
|
|
if (IS_ERR(opaquedir))
|
|
goto out;
|
|
}
|
|
|
|
inode_lock_nested(dir, I_MUTEX_PARENT);
|
|
upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
|
|
dentry->d_name.len);
|
|
err = PTR_ERR(upper);
|
|
if (IS_ERR(upper))
|
|
goto out_unlock;
|
|
|
|
err = -ESTALE;
|
|
if ((opaquedir && upper != opaquedir) ||
|
|
(!opaquedir && !ovl_matches_upper(dentry, upper)))
|
|
goto out_dput_upper;
|
|
|
|
if (is_dir)
|
|
err = ovl_do_rmdir(ofs, dir, upper);
|
|
else
|
|
err = ovl_do_unlink(ofs, dir, upper);
|
|
ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry));
|
|
|
|
/*
|
|
* Keeping this dentry hashed would mean having to release
|
|
* upperpath/lowerpath, which could only be done if we are the
|
|
* sole user of this dentry. Too tricky... Just unhash for
|
|
* now.
|
|
*/
|
|
if (!err)
|
|
d_drop(dentry);
|
|
out_dput_upper:
|
|
dput(upper);
|
|
out_unlock:
|
|
inode_unlock(dir);
|
|
dput(opaquedir);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static bool ovl_pure_upper(struct dentry *dentry)
|
|
{
|
|
return !ovl_dentry_lower(dentry) &&
|
|
!ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
|
|
}
|
|
|
|
static void ovl_drop_nlink(struct dentry *dentry)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
struct dentry *alias;
|
|
|
|
/* Try to find another, hashed alias */
|
|
spin_lock(&inode->i_lock);
|
|
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
|
|
if (alias != dentry && !d_unhashed(alias))
|
|
break;
|
|
}
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
/*
|
|
* Changes to underlying layers may cause i_nlink to lose sync with
|
|
* reality. In this case prevent the link count from going to zero
|
|
* prematurely.
|
|
*/
|
|
if (inode->i_nlink > !!alias)
|
|
drop_nlink(inode);
|
|
}
|
|
|
|
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
|
{
|
|
int err;
|
|
const struct cred *old_cred;
|
|
bool lower_positive = ovl_lower_positive(dentry);
|
|
LIST_HEAD(list);
|
|
|
|
/* No need to clean pure upper removed by vfs_rmdir() */
|
|
if (is_dir && (lower_positive || !ovl_pure_upper(dentry))) {
|
|
err = ovl_check_empty_dir(dentry, &list);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
err = ovl_want_write(dentry);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = ovl_copy_up(dentry->d_parent);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
err = ovl_nlink_start(dentry);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
old_cred = ovl_override_creds(dentry->d_sb);
|
|
if (!lower_positive)
|
|
err = ovl_remove_upper(dentry, is_dir, &list);
|
|
else
|
|
err = ovl_remove_and_whiteout(dentry, &list);
|
|
ovl_revert_creds(dentry->d_sb, old_cred);
|
|
if (!err) {
|
|
if (is_dir)
|
|
clear_nlink(dentry->d_inode);
|
|
else
|
|
ovl_drop_nlink(dentry);
|
|
}
|
|
ovl_nlink_end(dentry);
|
|
|
|
/*
|
|
* Copy ctime
|
|
*
|
|
* Note: we fail to update ctime if there was no copy-up, only a
|
|
* whiteout
|
|
*/
|
|
if (ovl_dentry_upper(dentry))
|
|
ovl_copyattr(d_inode(dentry));
|
|
|
|
out_drop_write:
|
|
ovl_drop_write(dentry);
|
|
out:
|
|
ovl_cache_free(&list);
|
|
return err;
|
|
}
|
|
|
|
static int ovl_unlink(struct inode *dir, struct dentry *dentry)
|
|
{
|
|
return ovl_do_remove(dentry, false);
|
|
}
|
|
|
|
static int ovl_rmdir(struct inode *dir, struct dentry *dentry)
|
|
{
|
|
return ovl_do_remove(dentry, true);
|
|
}
|
|
|
|
static bool ovl_type_merge_or_lower(struct dentry *dentry)
|
|
{
|
|
enum ovl_path_type type = ovl_path_type(dentry);
|
|
|
|
return OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type);
|
|
}
|
|
|
|
static bool ovl_can_move(struct dentry *dentry)
|
|
{
|
|
return ovl_redirect_dir(dentry->d_sb) ||
|
|
!d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
|
|
}
|
|
|
|
static char *ovl_get_redirect(struct dentry *dentry, bool abs_redirect)
|
|
{
|
|
char *buf, *ret;
|
|
struct dentry *d, *tmp;
|
|
int buflen = ovl_redirect_max + 1;
|
|
|
|
if (!abs_redirect) {
|
|
ret = kstrndup(dentry->d_name.name, dentry->d_name.len,
|
|
GFP_KERNEL);
|
|
goto out;
|
|
}
|
|
|
|
buf = ret = kmalloc(buflen, GFP_KERNEL);
|
|
if (!buf)
|
|
goto out;
|
|
|
|
buflen--;
|
|
buf[buflen] = '\0';
|
|
for (d = dget(dentry); !IS_ROOT(d);) {
|
|
const char *name;
|
|
int thislen;
|
|
|
|
spin_lock(&d->d_lock);
|
|
name = ovl_dentry_get_redirect(d);
|
|
if (name) {
|
|
thislen = strlen(name);
|
|
} else {
|
|
name = d->d_name.name;
|
|
thislen = d->d_name.len;
|
|
}
|
|
|
|
/* If path is too long, fall back to userspace move */
|
|
if (thislen + (name[0] != '/') > buflen) {
|
|
ret = ERR_PTR(-EXDEV);
|
|
spin_unlock(&d->d_lock);
|
|
goto out_put;
|
|
}
|
|
|
|
buflen -= thislen;
|
|
memcpy(&buf[buflen], name, thislen);
|
|
spin_unlock(&d->d_lock);
|
|
tmp = dget_parent(d);
|
|
|
|
dput(d);
|
|
d = tmp;
|
|
|
|
/* Absolute redirect: finished */
|
|
if (buf[buflen] == '/')
|
|
break;
|
|
buflen--;
|
|
buf[buflen] = '/';
|
|
}
|
|
ret = kstrdup(&buf[buflen], GFP_KERNEL);
|
|
out_put:
|
|
dput(d);
|
|
kfree(buf);
|
|
out:
|
|
return ret ? ret : ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
static bool ovl_need_absolute_redirect(struct dentry *dentry, bool samedir)
|
|
{
|
|
struct dentry *lowerdentry;
|
|
|
|
if (!samedir)
|
|
return true;
|
|
|
|
if (d_is_dir(dentry))
|
|
return false;
|
|
|
|
/*
|
|
* For non-dir hardlinked files, we need absolute redirects
|
|
* in general as two upper hardlinks could be in different
|
|
* dirs. We could put a relative redirect now and convert
|
|
* it to absolute redirect later. But when nlink > 1 and
|
|
* indexing is on, that means relative redirect needs to be
|
|
* converted to absolute during copy up of another lower
|
|
* hardllink as well.
|
|
*
|
|
* So without optimizing too much, just check if lower is
|
|
* a hard link or not. If lower is hard link, put absolute
|
|
* redirect.
|
|
*/
|
|
lowerdentry = ovl_dentry_lower(dentry);
|
|
return (d_inode(lowerdentry)->i_nlink > 1);
|
|
}
|
|
|
|
static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
|
{
|
|
int err;
|
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
|
const char *redirect = ovl_dentry_get_redirect(dentry);
|
|
bool absolute_redirect = ovl_need_absolute_redirect(dentry, samedir);
|
|
|
|
if (redirect && (!absolute_redirect || redirect[0] == '/'))
|
|
return 0;
|
|
|
|
redirect = ovl_get_redirect(dentry, absolute_redirect);
|
|
if (IS_ERR(redirect))
|
|
return PTR_ERR(redirect);
|
|
|
|
err = ovl_check_setxattr(ofs, ovl_dentry_upper(dentry),
|
|
OVL_XATTR_REDIRECT,
|
|
redirect, strlen(redirect), -EXDEV);
|
|
if (!err) {
|
|
spin_lock(&dentry->d_lock);
|
|
ovl_dentry_set_redirect(dentry, redirect);
|
|
spin_unlock(&dentry->d_lock);
|
|
} else {
|
|
kfree(redirect);
|
|
pr_warn_ratelimited("failed to set redirect (%i)\n",
|
|
err);
|
|
/* Fall back to userspace copy-up */
|
|
err = -EXDEV;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
|
|
struct dentry *old, struct inode *newdir,
|
|
struct dentry *new, unsigned int flags)
|
|
{
|
|
int err;
|
|
struct dentry *old_upperdir;
|
|
struct dentry *new_upperdir;
|
|
struct dentry *olddentry;
|
|
struct dentry *newdentry;
|
|
struct dentry *trap;
|
|
bool old_opaque;
|
|
bool new_opaque;
|
|
bool cleanup_whiteout = false;
|
|
bool update_nlink = false;
|
|
bool overwrite = !(flags & RENAME_EXCHANGE);
|
|
bool is_dir = d_is_dir(old);
|
|
bool new_is_dir = d_is_dir(new);
|
|
bool samedir = olddir == newdir;
|
|
struct dentry *opaquedir = NULL;
|
|
const struct cred *old_cred = NULL;
|
|
struct ovl_fs *ofs = OVL_FS(old->d_sb);
|
|
LIST_HEAD(list);
|
|
|
|
err = -EINVAL;
|
|
if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
|
|
goto out;
|
|
|
|
flags &= ~RENAME_NOREPLACE;
|
|
|
|
/* Don't copy up directory trees */
|
|
err = -EXDEV;
|
|
if (!ovl_can_move(old))
|
|
goto out;
|
|
if (!overwrite && !ovl_can_move(new))
|
|
goto out;
|
|
|
|
if (overwrite && new_is_dir && !ovl_pure_upper(new)) {
|
|
err = ovl_check_empty_dir(new, &list);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
if (overwrite) {
|
|
if (ovl_lower_positive(old)) {
|
|
if (!ovl_dentry_is_whiteout(new)) {
|
|
/* Whiteout source */
|
|
flags |= RENAME_WHITEOUT;
|
|
} else {
|
|
/* Switch whiteouts */
|
|
flags |= RENAME_EXCHANGE;
|
|
}
|
|
} else if (is_dir && ovl_dentry_is_whiteout(new)) {
|
|
flags |= RENAME_EXCHANGE;
|
|
cleanup_whiteout = true;
|
|
}
|
|
}
|
|
|
|
err = ovl_want_write(old);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = ovl_copy_up(old);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
err = ovl_copy_up(new->d_parent);
|
|
if (err)
|
|
goto out_drop_write;
|
|
if (!overwrite) {
|
|
err = ovl_copy_up(new);
|
|
if (err)
|
|
goto out_drop_write;
|
|
} else if (d_inode(new)) {
|
|
err = ovl_nlink_start(new);
|
|
if (err)
|
|
goto out_drop_write;
|
|
|
|
update_nlink = true;
|
|
}
|
|
|
|
old_cred = ovl_override_creds(old->d_sb);
|
|
|
|
if (!list_empty(&list)) {
|
|
opaquedir = ovl_clear_empty(new, &list);
|
|
err = PTR_ERR(opaquedir);
|
|
if (IS_ERR(opaquedir)) {
|
|
opaquedir = NULL;
|
|
goto out_revert_creds;
|
|
}
|
|
}
|
|
|
|
old_upperdir = ovl_dentry_upper(old->d_parent);
|
|
new_upperdir = ovl_dentry_upper(new->d_parent);
|
|
|
|
if (!samedir) {
|
|
/*
|
|
* When moving a merge dir or non-dir with copy up origin into
|
|
* a new parent, we are marking the new parent dir "impure".
|
|
* When ovl_iterate() iterates an "impure" upper dir, it will
|
|
* lookup the origin inodes of the entries to fill d_ino.
|
|
*/
|
|
if (ovl_type_origin(old)) {
|
|
err = ovl_set_impure(new->d_parent, new_upperdir);
|
|
if (err)
|
|
goto out_revert_creds;
|
|
}
|
|
if (!overwrite && ovl_type_origin(new)) {
|
|
err = ovl_set_impure(old->d_parent, old_upperdir);
|
|
if (err)
|
|
goto out_revert_creds;
|
|
}
|
|
}
|
|
|
|
trap = lock_rename(new_upperdir, old_upperdir);
|
|
|
|
olddentry = ovl_lookup_upper(ofs, old->d_name.name, old_upperdir,
|
|
old->d_name.len);
|
|
err = PTR_ERR(olddentry);
|
|
if (IS_ERR(olddentry))
|
|
goto out_unlock;
|
|
|
|
err = -ESTALE;
|
|
if (!ovl_matches_upper(old, olddentry))
|
|
goto out_dput_old;
|
|
|
|
newdentry = ovl_lookup_upper(ofs, new->d_name.name, new_upperdir,
|
|
new->d_name.len);
|
|
err = PTR_ERR(newdentry);
|
|
if (IS_ERR(newdentry))
|
|
goto out_dput_old;
|
|
|
|
old_opaque = ovl_dentry_is_opaque(old);
|
|
new_opaque = ovl_dentry_is_opaque(new);
|
|
|
|
err = -ESTALE;
|
|
if (d_inode(new) && ovl_dentry_upper(new)) {
|
|
if (opaquedir) {
|
|
if (newdentry != opaquedir)
|
|
goto out_dput;
|
|
} else {
|
|
if (!ovl_matches_upper(new, newdentry))
|
|
goto out_dput;
|
|
}
|
|
} else {
|
|
if (!d_is_negative(newdentry)) {
|
|
if (!new_opaque || !ovl_is_whiteout(newdentry))
|
|
goto out_dput;
|
|
} else {
|
|
if (flags & RENAME_EXCHANGE)
|
|
goto out_dput;
|
|
}
|
|
}
|
|
|
|
if (olddentry == trap)
|
|
goto out_dput;
|
|
if (newdentry == trap)
|
|
goto out_dput;
|
|
|
|
if (olddentry->d_inode == newdentry->d_inode)
|
|
goto out_dput;
|
|
|
|
err = 0;
|
|
if (ovl_type_merge_or_lower(old))
|
|
err = ovl_set_redirect(old, samedir);
|
|
else if (is_dir && !old_opaque && ovl_type_merge(new->d_parent))
|
|
err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
|
|
if (err)
|
|
goto out_dput;
|
|
|
|
if (!overwrite && ovl_type_merge_or_lower(new))
|
|
err = ovl_set_redirect(new, samedir);
|
|
else if (!overwrite && new_is_dir && !new_opaque &&
|
|
ovl_type_merge(old->d_parent))
|
|
err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
|
|
if (err)
|
|
goto out_dput;
|
|
|
|
err = ovl_do_rename(ofs, old_upperdir->d_inode, olddentry,
|
|
new_upperdir->d_inode, newdentry, flags);
|
|
if (err)
|
|
goto out_dput;
|
|
|
|
if (cleanup_whiteout)
|
|
ovl_cleanup(ofs, old_upperdir->d_inode, newdentry);
|
|
|
|
if (overwrite && d_inode(new)) {
|
|
if (new_is_dir)
|
|
clear_nlink(d_inode(new));
|
|
else
|
|
ovl_drop_nlink(new);
|
|
}
|
|
|
|
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||
|
|
(!overwrite && ovl_type_origin(new)));
|
|
ovl_dir_modified(new->d_parent, ovl_type_origin(old) ||
|
|
(d_inode(new) && ovl_type_origin(new)));
|
|
|
|
/* copy ctime: */
|
|
ovl_copyattr(d_inode(old));
|
|
if (d_inode(new) && ovl_dentry_upper(new))
|
|
ovl_copyattr(d_inode(new));
|
|
|
|
out_dput:
|
|
dput(newdentry);
|
|
out_dput_old:
|
|
dput(olddentry);
|
|
out_unlock:
|
|
unlock_rename(new_upperdir, old_upperdir);
|
|
out_revert_creds:
|
|
ovl_revert_creds(old->d_sb, old_cred);
|
|
if (update_nlink)
|
|
ovl_nlink_end(new);
|
|
out_drop_write:
|
|
ovl_drop_write(old);
|
|
out:
|
|
dput(opaquedir);
|
|
ovl_cache_free(&list);
|
|
return err;
|
|
}
|
|
|
|
const struct inode_operations ovl_dir_inode_operations = {
|
|
.lookup = ovl_lookup,
|
|
.mkdir = ovl_mkdir,
|
|
.symlink = ovl_symlink,
|
|
.unlink = ovl_unlink,
|
|
.rmdir = ovl_rmdir,
|
|
.rename = ovl_rename,
|
|
.link = ovl_link,
|
|
.setattr = ovl_setattr,
|
|
.create = ovl_create,
|
|
.mknod = ovl_mknod,
|
|
.permission = ovl_permission,
|
|
.getattr = ovl_getattr,
|
|
.listxattr = ovl_listxattr,
|
|
.get_acl = ovl_get_acl,
|
|
.update_time = ovl_update_time,
|
|
.fileattr_get = ovl_fileattr_get,
|
|
.fileattr_set = ovl_fileattr_set,
|
|
};
|