android_kernel_xiaomi_sm8450/fs/f2fs/file.c
Greg Kroah-Hartman b0e9b554c3 Merge tag 'android12-5.10.228_r00' into android12-5.10
This merges up to the 5.10.228 LTS release into the android12-5.10
branch.  Changes included in here are:

* 38dc270ca0 Revert "genetlink: hold RCU in genlmsg_mcast()"
*   c515597aec Merge 02874ca52d ("tracing: Consider the NULL character when validating the event length") into android12-5.10-lts
|\
| * 02874ca52d tracing: Consider the NULL character when validating the event length
| * df848523d6 jfs: Fix sanity check in dbMount
| * 8605ca4bd0 arm64: Force position-independent veneers
| * 0329056e07 ASoC: fsl_sai: Enable 'FIFO continue on error' FCONT bit
| * 75f828e944 drm/vboxvideo: Replace fake VLA at end of vbva_mouse_pointer_shape with real VLA
| * 649d646506 iomap: update ki_pos a little later in iomap_dio_complete
| * c9b7743807 exec: don't WARN for racy path_noexec check
| * 20e27c7739 block, bfq: fix procress reference leakage for bfqq in merge chain
| * bf83ba3c55 KVM: s390: gaccess: Check if guest address is in memslot
| * 6e1659b674 KVM: s390: gaccess: Cleanup access to guest pages
| * 472088ffb1 KVM: s390: gaccess: Refactor access address range check
| * 511ca93509 KVM: s390: gaccess: Refactor gpa and length calculation
| * cf9ddf9ed9 arm64: probes: Fix uprobes for big-endian kernels
| * 7f1ef59185 arm64:uprobe fix the uprobe SWBP_INSN in big-endian
| * 6c151aeb6d Bluetooth: bnep: fix wild-memory-access in proto_unregister
| * 5c345c47e8 s390: Initialize psw mask in perf_arch_fetch_caller_regs()
| * 6af43ec3bf usb: typec: altmode should keep reference to parent
| * ed31aba8ce smb: client: fix OOBs when building SMB2_IOCTL request
| * 8c1e6717f6 scsi: target: core: Fix null-ptr-deref in target_alloc_device()
| * 4af714e823 genetlink: hold RCU in genlmsg_mcast()
| * b632114677 net: systemport: fix potential memory leak in bcm_sysport_xmit()
| * f48eaf4e88 net/smc: Fix searching in list of known pnetids in smc_pnet_add_pnetid
| * aacf6e28ae net: ethernet: aeroflex: fix potential memory leak in greth_start_xmit_gbit()
| * 56dbb74b6a macsec: don't increment counters for an unrelated SA
| * 2c7dd3ca6b drm/msm/dsi: fix 32-bit signed integer extension in pclk_rate calculation
| * df6fed0a2a RDMA/bnxt_re: Fix a bug while setting up Level-2 PBL pages
| * 78aaf54ad5 RDMA/bnxt_re: Return more meaningful error
| * 718609f518 ipv4: give an IPv4 dev to blackhole_netdev
| * 59df170bde RDMA/cxgb4: Fix RDMA_CM_EVENT_UNREACHABLE error for iWARP
| * 9076d449e7 ARM: dts: bcm2837-rpi-cm3-io3: Fix HDMI hpd-gpio pin
| * dbe51dd516 RDMA/bnxt_re: Add a check for memory allocation
| * c17e5cbbb1 RDMA/bnxt_re: Fix incorrect AVID type in WQE structure
* | 012423e6bd Merge 5.10.228 into android12-5.10-lts
|\|
| * 5a8fa04b2a Linux 5.10.228
| * 2abe57d62a ALSA: hda/conexant - Use cached pin control for Node 0x1d on HP EliteOne 1000 G2
| * 25e86fb0ae powerpc/mm: Always update max/min_low_pfn in mem_topology_setup()
| * c1d0476885 nilfs2: propagate directory read errors from nilfs_find_entry()
| * c38add9ac0 tcp: fix mptcp DSS corruption due to large pmtu xmit
| * fde99e972b mptcp: handle consistently DSS corruption
| * 609937aa96 mptcp: track and update contiguous data status
| * b7d7b7fc87 irqchip/gic-v4: Don't allow a VMOVP on a dying VPE
| * 6f44a5fc15 x86/entry_32: Clear CPU buffers after register restore in NMI return
| * 9ab38a1cdb x86/entry_32: Do not clobber user EFLAGS.ZF
| * 8462805788 x86/apic: Always explicitly disarm TSC-deadline timer
| * e475220d64 x86/resctrl: Annotate get_mem_config() functions as __init
| * 1826b6d69b parport: Proper fix for array out-of-bounds access
| * 9f8ddf14fa USB: serial: option: add Telit FN920C04 MBIM compositions
| * 0fc55ec9fc USB: serial: option: add support for Quectel EG916Q-GL
| * 608b626f71 xhci: Fix incorrect stream context type macro
| * fc2cb5e3af Bluetooth: btusb: Fix regression with fake CSR controllers 0a12:0001
| * 63d6a3b078 Bluetooth: Remove debugfs directory on module init failure
| * 516655749a iio: adc: ti-ads124s08: add missing select IIO_(TRIGGERED_)BUFFER in Kconfig
| * f80375f275 iio: proximity: mb1232: add missing select IIO_(TRIGGERED_)BUFFER in Kconfig
| * dc99dfa2ba iio: light: opt3001: add missing full-scale range value
| * bf3ab8e1c2 iio: light: veml6030: fix IIO device retrieval from embedded device
| * 2d06787b70 iio: light: veml6030: fix ALS sensor resolution
| * ffc4174309 iio: hid-sensors: Fix an error handling path in _hid_sensor_set_report_latency()
| * 9504153a48 iio: adc: ti-ads8688: add missing select IIO_(TRIGGERED_)BUFFER in Kconfig
| * c27133637a iio: dac: stm32-dac-core: add missing select REGMAP_MMIO in Kconfig
| * c4c2211b12 iio: dac: ltc1660: add missing select REGMAP_SPI in Kconfig
| * 3abc1ebea3 iio: dac: ad5770r: add missing select REGMAP_SPI in Kconfig
| * 0782809c01 drm/vmwgfx: Handle surface check failure correctly
| * df75c78bfe drm/radeon: Fix encoder->possible_clones
| * 6ddcaee244 io_uring/sqpoll: close race on waiting for sqring entries
| * 3bc6d0f8b7 blk-rq-qos: fix crash on rq_qos_wait vs. rq_qos_wake_function race
| * eca3edf876 x86/bugs: Do not use UNTRAIN_RET with IBPB on entry
| * e7c0f8ca3b x86/bugs: Skip RSB fill at VMEXIT
| * 0ab77a47e3 x86/entry: Have entry_ibpb() invalidate return predictions
| * c5e57863d7 x86/cpufeatures: Add a IBPB_NO_RET BUG flag
| * 77fa260620 x86/cpufeatures: Define X86_FEATURE_AMD_IBPB_RET
| * dfa4b5d4ba KVM: s390: Change virtual to physical address access in diag 0x258 handler
| * d047095095 s390/sclp_vt220: Convert newlines to CRLF instead of LFCR
| * b12ef2d4df io_uring/sqpoll: do not put cpumask on stack
| * 66b98c4f18 io_uring/sqpoll: retain test for whether the CPU is valid
| * 54a987b41d io_uring/sqpoll: do not allow pinning outside of cpuset
| * 71fbc3af3d KVM: Fix a data race on last_boosted_vcpu in kvm_vcpu_on_spin()
| * e8e599a635 wifi: mac80211: fix potential key use-after-free
| * 417d5838ca mm/swapfile: skip HugeTLB pages for unuse_vma
| * 043f055261 fat: fix uninitialized variable
| * bf1a022222 irqchip/gic-v3-its: Fix VSYNC referencing an unmapped VPE on GIC v4.1
| * cafa5942bd net: macb: Avoid 20s boot delay by skipping MDIO bus registration for fixed-link PHY
| * ce43c48cdc arm64: probes: Fix simulate_ldr*_literal()
| * 3728b4eb27 arm64: probes: Remove broken LDR (literal) uprobe support
| * 673a1c5a29 posix-clock: Fix missing timespec64 check in pc_clock_settime()
| * dbe055567a net: enetc: add missing static descriptor and inline keyword
| * 5f47cdeeef ALSA: hda/conexant - Fix audio routing for HP EliteOne 1000 G2
* | af2f7573ea Revert "xfrm: Pass flowi_oif or l3mdev as oif to xfrm_dst_lookup"
* | 705b091042 Revert "net: Handle l3mdev in ip_tunnel_init_flow"
* | 0ba4653710 Merge 5.10.227 into android12-5.10-lts
|\|
| * eac1c5bfc1 Linux 5.10.227
| * 9350016415 net: dsa: microchip: fix build warning
| * 9f76a9d184 RDMA/hns: Fix uninitialized variable
* | 7ad1ad5a55 Merge 4911610c7a ("ext4: fix warning in ext4_dio_write_end_io()") into android12-5.10-lts
|\|
| * 4911610c7a ext4: fix warning in ext4_dio_write_end_io()
* | b585ecc2c9 Merge 05cc42d601 ("netfilter: ip6t_rpfilter: Fix regression with VRF interfaces") into android12-5.10-lts
|\|
| * 05cc42d601 netfilter: ip6t_rpfilter: Fix regression with VRF interfaces
| * 95f62e5a78 net: vrf: determine the dst using the original ifindex for multicast
| * 3adb1be04f net: seg6: fix seg6_lookup_any_nexthop() to handle VRFs using flowi_l3mdev
| * ab6c9463b1 net: Handle l3mdev in ip_tunnel_init_flow
| * 0825c5ff24 xfrm: Pass flowi_oif or l3mdev as oif to xfrm_dst_lookup
| * 4bf1bd3fff net: geneve: add missing netlink policy and size for IFLA_GENEVE_INNER_PROTO_INHERIT
| * 73f75d2b5a nouveau/dmem: Fix vulnerability in migrate_to_ram upon copy error
| * 548d0102dc net: dsa: lan9303: ensure chip reset and wait for READY status
| * 68ad5da6ca net: Fix an unsafe loop on the list
| * fcda074c98 hid: intel-ish-hid: Fix uninitialized variable 'rv' in ish_fw_xfer_direct_dma
| * e7a1d51b39 usb: storage: ignore bogus device raised by JieLi BR21 USB sound chip
| * 1d7fc802a7 usb: xhci: Fix problem with xhci resume from suspend
| * f8dea2fede usb: dwc3: core: Stop processing of pending events if controller is halted
* | 9b1caf0550 Merge a7564b1606 ("Revert "usb: yurex: Replace snprintf() with the safer scnprintf() variant"") into android12-5.10-lts
|\|
| * a7564b1606 Revert "usb: yurex: Replace snprintf() with the safer scnprintf() variant"
| * 8669bca53f HID: plantronics: Workaround for an unexcepted opposite volume key
| * 799a06ca7f hwmon: (adm9240) Add missing dependency on REGMAP_I2C
| * ab6bc15e99 hwmon: (tmp513) Add missing dependency on REGMAP_I2C
| * 1d5f85f1b7 resource: fix region_intersects() vs add_memory_driver_managed()
| * 36b054324d slip: make slhc_remember() more robust against malicious packets
| * 30d91a478d ppp: fix ppp_async_encode() illegal access
* | 4098b69102 Revert "net: Add l3mdev index to flow struct and avoid oif reset for port devices"
* | 5cb5d15755 Revert "netfilter: rpfilter/fib: Populate flowic_l3mdev field"
* | 6fc602a28c Revert "netfilter: rpfilter/fib: Set ->flowic_uid correctly for user namespaces."
* | 7253529039 Revert "netfilter: fib: check correct rtable in vrf setups"
* | 81d8cb7a3e Merge 3502b1a297 ("netfilter: fib: check correct rtable in vrf setups") into android12-5.10-lts
|\|
| * 3502b1a297 netfilter: fib: check correct rtable in vrf setups
| * 037145e2a2 netfilter: rpfilter/fib: Set ->flowic_uid correctly for user namespaces.
| * d98558fe26 netfilter: rpfilter/fib: Populate flowic_l3mdev field
| * 740de19877 net: Add l3mdev index to flow struct and avoid oif reset for port devices
* | ff0e27a0f9 Merge 265bf63e24 ("sctp: ensure sk_state is set to CLOSED if hashing fails in sctp_listen_start") into android12-5.10-lts
|\|
| * 265bf63e24 sctp: ensure sk_state is set to CLOSED if hashing fails in sctp_listen_start
| * 23e139f90b net: ibm: emac: mal: fix wrong goto
| * adbc3eef43 net/sched: accept TCA_STAB only for root qdisc
| * d79af3af2f igb: Do not bring the device up after non-fatal error
| * c70e05b929 gpio: aspeed: Use devm_clk api to manage clock source
| * 36fd66cb51 gpio: aspeed: Add the flush write to ensure the write complete.
* | ca21e0d3a8 Merge 0e91c4b484 ("net: dsa: b53: fix jumbo frames on 10/100 ports") into android12-5.10-lts
|\|
| * 0e91c4b484 net: dsa: b53: fix jumbo frames on 10/100 ports
| * a7c9402bbc net: dsa: b53: allow lower MTUs on BCM5325/5365
| * 9f3407aa6c net: dsa: b53: fix max MTU for BCM5325/BCM5365
| * 0109267c1e net: dsa: b53: fix max MTU for 1g switches
| * a625acf221 net: dsa: b53: fix jumbo frame mtu check
| * e13ffbf5fd net: phy: bcm84881: Fix some error handling paths
| * ef44274dae Bluetooth: RFCOMM: FIX possible deadlock in rfcomm_sk_state_change
| * cce8419b81 netfilter: br_netfilter: fix panic with metadata_dst skb
| * 8a517d1845 tcp: fix tcp_enter_recovery() to zero retrans_stamp when it's safe
| * fe238ddf85 tcp: fix to allow timestamp undo if no retransmits were sent
* | 6c71f56f4f Merge 21b5af7f0c ("net: phy: dp83869: fix memory corruption when enabling fiber") into android12-5.10-lts
|\|
| * 21b5af7f0c net: phy: dp83869: fix memory corruption when enabling fiber
| * f892165c56 NFSv4: Prevent NULL-pointer dereference in nfs42_complete_copies()
| * 64f1b4922b SUNRPC: Fix integer overflow in decode_rc_list()
| * 1fc13f6a41 ice: fix VLAN replay after reset
| * 993ce09fe6 NFSD: Mark filecache "down" if init fails
* | e22e091f95 Merge de5a059e36 ("RDMA/rxe: Fix seg fault in rxe_comp_queue_pkt") into android12-5.10-lts
|\|
| * de5a059e36 RDMA/rxe: Fix seg fault in rxe_comp_queue_pkt
| * 252f147b18 fbdev: sisfb: Fix strbuf array overflow
| * f4149eec96 drm/amd/display: Check null pointer before dereferencing se
| * 56452dbc0a driver core: bus: Return -EIO instead of 0 when show/store invalid bus attribute
| * d8ac7378bc tools/iio: Add memory allocation failure check for trigger_name
| * 4ce662fe4b virtio_pmem: Check device status before requesting flush
| * 30ea38665d usb: dwc2: Adjust the timing of USB Driver Interrupt Registration in the Crashkernel Scenario
| * 4c83143fc6 usb: chipidea: udc: enable suspend interrupt after usb reset
| * b677b94a91 clk: imx: Remove CLK_SET_PARENT_GATE for DRAM mux for i.MX7D
| * 37c181e389 media: videobuf2-core: clear memory related fields in __vb2_plane_dmabuf_put()
| * b650189687 ntb: ntb_hw_switchtec: Fix use after free vulnerability in switchtec_ntb_remove due to race condition
| * 63047187a5 PCI: Mark Creative Labs EMU20k2 INTx masking as broken
| * a28703d4d4 i2c: i801: Use a different adapter-name for IDF adapters
| * e4f218455c PCI: Add ACS quirk for Qualcomm SA8775P
| * f3e0a8b7d4 clk: bcm: bcm53573: fix OF node leak in init
| * b10c1ca941 PCI: Add function 0 DMA alias quirk for Glenfly Arise chip
| * 713adaf0ec RDMA/mad: Improve handling of timed out WRs of mad agent
| * 24318116c4 ktest.pl: Avoid false positives with grub2 skip regex
| * 345d3c0bf2 s390/cpum_sf: Remove WARN_ON_ONCE statements
| * c2097d5efb ext4: nested locking for xattr inode
| * dced2c9d66 s390/mm: Add cond_resched() to cmm_alloc/free_pages()
| * 1cd197813e s390/facility: Disable compile time optimization for decompressor code
| * 029aa36ba3 bpf: Check percpu map value size first
| * 4f4a6d70d7 Input: synaptics-rmi4 - fix UAF of IRQ domain on driver removal
| * 546fb43a2e tracing/kprobes: Fix symbol counting logic by looking at modules as well
| * 90a6a070a8 tracing/kprobes: Return EADDRNOTAVAIL when func matches several symbols
* | 2a22a03cae Merge 2622c805ab ("kallsyms: Make module_kallsyms_on_each_symbol generally available") into android12-5.10-lts
|\|
| * 2622c805ab kallsyms: Make module_kallsyms_on_each_symbol generally available
| * 2aa861ec72 kallsyms: Make kallsyms_on_each_symbol generally available
| * 9b82d737d2 virtio_console: fix misc probe bugs
| * f2fd1a9597 tracing: Have saved_cmdlines arrays all in one allocation
| * 25b0021620 s390/zcore: release dump save area on restart or power down
| * 0b4dc46f87 s390/zcore: no need to check return value of debugfs_create functions
| * 421795b064 drm/crtc: fix uninitialized variable use even harder
| * a009e88cc9 tracing: Remove precision vsnprintf() check from print event
| * 6063d72b61 net: ethernet: cortina: Drop TSO support
| * 39fffca572 unicode: Don't special case ignorable code points
| * 6592347f06 vhost/scsi: null-ptr-dereference in vhost_scsi_get_req()
| * 2f6da71e9d ext4: fix inode tree inconsistency caused by ENOMEM
| * da964de4c1 ACPI: battery: Fix possible crash when unregistering a battery hook
| * 20557232c9 ACPI: battery: Simplify battery hook locking
| * 991e8b0bab r8169: add tally counter fields added with RTL8125
| * 238d5c541e r8169: Fix spelling mistake: "tx_underun" -> "tx_underrun"
| * 281edfa1cd clk: qcom: dispcc-sm8250: use CLK_SET_RATE_PARENT for branch clocks
* | 96a5139526 Merge 570e257621 ("drm/rockchip: vop: clear DMA stop bit on RK3066") into android12-5.10-lts
|\|
| * 570e257621 drm/rockchip: vop: clear DMA stop bit on RK3066
| * 411e2e1d01 drm/rockchip: support gamma control on RK3399
| * 1aeaa7e8d8 drm/rockchip: define gamma registers for RK3399
| * f561b48d63 uprobes: fix kernel info leak via "[uprobes]" vma
* | 042d3e2676 Merge 24afda0421 ("arm64: errata: Expand speculative SSBS workaround once more") into android12-5.10-lts
|\|
| * 24afda0421 arm64: errata: Expand speculative SSBS workaround once more
| * 9df353ca13 arm64: cputype: Add Neoverse-N3 definitions
| * c45edd5942 arm64: Add Cortex-715 CPU part definition
* | ce691439c0 Revert "ext4: properly sync file size update after O_SYNC direct IO"
* | f46870ab3d Revert "ext4: dax: fix overflowing extents beyond inode size when partially writing"
* | 5d9c84863f Merge f8a7c34232 ("ext4: dax: fix overflowing extents beyond inode size when partially writing") into android12-5.10-lts
|\|
| * f8a7c34232 ext4: dax: fix overflowing extents beyond inode size when partially writing
* | 8fb88ba5de Merge dde4c1e166 ("ext4: properly sync file size update after O_SYNC direct IO") into android12-5.10-lts
|\|
| * dde4c1e166 ext4: properly sync file size update after O_SYNC direct IO
* | e62d85f9ba Merge 6ff56ef7f7 ("i2c: xiic: Fix pm_runtime_set_suspended() with runtime pm enabled") into android12-5.10-lts
|\|
| * 6ff56ef7f7 i2c: xiic: Fix pm_runtime_set_suspended() with runtime pm enabled
* | b5e0cda160 Merge d223126bb0 ("i2c: xiic: Use devm_clk_get_enabled()") into android12-5.10-lts
|\|
| * d223126bb0 i2c: xiic: Use devm_clk_get_enabled()
* | ed3c358943 Merge 9bd3443e34 ("i2c: xiic: Simplify with dev_err_probe()") into android12-5.10-lts
|\|
| * 9bd3443e34 i2c: xiic: Simplify with dev_err_probe()
* | 93d28c0f5a Merge 8b55076b7b ("kconfig: qconf: fix buffer overflow in debug links") into android12-5.10-lts
|\|
| * 8b55076b7b kconfig: qconf: fix buffer overflow in debug links
* | 6e988ae353 Merge bfab5fbc12 ("drm/sched: Add locking to drm_sched_entity_modify_sched") into android12-5.10-lts
|\|
| * bfab5fbc12 drm/sched: Add locking to drm_sched_entity_modify_sched
* | 25d36c65fb Merge c54aa7d750 ("gpio: davinci: fix lazy disable") into android12-5.10-lts
|\|
| * c54aa7d750 gpio: davinci: fix lazy disable
* | 9fbdcfe7bb Merge 70b60c8d9b ("btrfs: wait for fixup workers before stopping cleaner kthread during umount") into android12-5.10-lts
|\|
| * 70b60c8d9b btrfs: wait for fixup workers before stopping cleaner kthread during umount
* | d476e18cbf Merge d73d48acf3 ("btrfs: fix a NULL pointer dereference when failed to start a new trasacntion") into android12-5.10-lts
|\|
| * d73d48acf3 btrfs: fix a NULL pointer dereference when failed to start a new trasacntion
* | 657f07546b Merge 67db431b85 ("ACPI: resource: Add Asus ExpertBook B2502CVA to irq1_level_low_skip_override[]") into android12-5.10-lts
|\|
| * 67db431b85 ACPI: resource: Add Asus ExpertBook B2502CVA to irq1_level_low_skip_override[]
* | 17c42250e8 Merge 1a819c7f85 ("ACPI: resource: Add Asus Vivobook X1704VAP to irq1_level_low_skip_override[]") into android12-5.10-lts
|\|
| * 1a819c7f85 ACPI: resource: Add Asus Vivobook X1704VAP to irq1_level_low_skip_override[]
* | c53240428e Revert "clk: qcom: clk-rpmh: Fix overflow in BCM vote"
* | 509ddbb2b8 Merge f976d964a6 ("Input: adp5589-keys - fix adp5589_gpio_get_value()") into android12-5.10-lts
|\|
| * f976d964a6 Input: adp5589-keys - fix adp5589_gpio_get_value()
| * bf8363e46f rtc: at91sam9: fix OF node leak in probe() error path
| * e33fe25b1e net: stmmac: Fix zero-division error when disabling tc cbs
| * 68e579316c tomoyo: fallback to realpath if symlink's pathname does not exist
| * 68c77a70e3 iio: magnetometer: ak8975: Fix reading for ak099xx sensors
| * 1d108095d5 clk: qcom: gcc-sm8250: Do not turn off PCIe GDSCs during gdsc_disable()
| * 60b6968341 media: venus: fix use after free bug in venus_remove due to race condition
| * 4445bc6e9f clk: qcom: clk-rpmh: Fix overflow in BCM vote
| * 3f73da56af media: uapi/linux/cec.h: cec_msg_set_reply_to: zero flags
| * 5443e70fb7 media: sun4i_csi: Implement link validate for sun4i_csi subdev
| * ed0d5103f9 clk: rockchip: fix error for unknown clocks
| * f63461af2c aoe: fix the potential use-after-free problem in more places
| * 7ae7ada29a NFSD: Fix NFSv4's PUTPUBFH operation
| * 0ea4333c67 nfsd: map the EBADMSG to nfserr_io to avoid warning
| * 96cad5da79 nfsd: fix delegation_blocked() to block correctly for at least 30 seconds
| * f692160d3e exfat: fix memory leak in exfat_load_bitmap()
| * e01e9ae43e riscv: define ILLEGAL_POINTER_VALUE for 64bit
| * 61b84013e5 ocfs2: fix possible null-ptr-deref in ocfs2_set_buffer_uptodate
| * bf605ae98d ocfs2: fix null-ptr-deref when journal load failed.
| * 3f1ca6ba54 ocfs2: remove unreasonable unlock in ocfs2_read_blocks
| * 14114d8148 ocfs2: cancel dqi_sync_work before freeing oinfo
| * aac31d654a ocfs2: reserve space for inline xattr before attaching reflink tree
| * 8e3bf36636 ocfs2: fix uninit-value in ocfs2_get_block()
| * ff1500fe26 ocfs2: fix the la space leak when unmounting an ocfs2 volume
| * a543785856 mm: krealloc: consider spare memory for __GFP_ZERO
| * 7fabacb337 jbd2: correctly compare tids with tid_geq function in jbd2_fc_begin_commit
| * 481e8f18a2 jbd2: stop waiting for space when jbd2_cleanup_journal_tail() returns error
| * 2bda897351 drm: omapdrm: Add missing check for alloc_ordered_workqueue
| * 3e8862875f of/irq: Support #msi-cells=<0> in of_msi_get_domain
| * a63fdf20cc parisc: Fix stack start for ADDR_NO_RANDOMIZE personality
| * ea7dead204 parisc: Fix 64-bit userspace syscall path
| * 25ec5c873c ext4: fix incorrect tid assumption in ext4_wait_for_tail_page_commit()
| * 6766937d03 ext4: update orig_path in ext4_find_extent()
| * b6c29c8f3d ext4: fix double brelse() the buffer of the extents path
| * 5e811066c5 ext4: aovid use-after-free in ext4_ext_insert_extent()
| * 8c26d9e53e ext4: drop ppath from ext4_ext_replay_update_ex() to avoid double-free
| * 4286a04183 ext4: fix incorrect tid assumption in __jbd2_log_wait_for_space()
| * 7bcdef04d0 ext4: propagate errors from ext4_find_extent() in ext4_insert_range()
| * e52f933598 ext4: fix slab-use-after-free in ext4_split_extent_at()
| * 133ff0d78f ext4: no need to continue when the number of entries is 1
* | 6a00671aec Merge 2c08dfc99f ("ALSA: hda/realtek: Add quirk for Huawei MateBook 13 KLV-WX9") into android12-5.10-lts
|\|
| * 2c08dfc99f ALSA: hda/realtek: Add quirk for Huawei MateBook 13 KLV-WX9
| * c36ff6948c ALSA: line6: add hw monitor volume control to POD HD500X
| * 64d315aeec ALSA: core: add isascii() check to card ID generator
| * b078a7eee1 drm: Consistently use struct drm_mode_rect for FB_DAMAGE_CLIPS
| * afa9990523 parisc: Fix itlb miss handler for 64-bit programs
| * d6b22a2d55 perf/core: Fix small negative period being ignored
| * 8ab638bb49 spi: bcm63xx: Fix module autoloading
| * f53c2b55d0 firmware: tegra: bpmp: Drop unused mbox_client_to_bpmp()
| * 2c1effc225 i2c: xiic: Wait for TX empty to avoid missed TX NAKs
| * 46c72b0139 i2c: qcom-geni: Use IRQF_NO_AUTOEN flag in request_irq()
| * 9b8bc33ad6 i2c: stm32f7: Do not prepare/unprepare clock during runtime suspend/resume
| * 10dde0c1fb selftests/mm: fix charge_reserved_hugetlb.sh test
| * e45803c71f selftests: vDSO: fix vDSO symbols lookup for powerpc64
| * 7624223155 selftests: breakpoints: use remaining time to check if suspend succeed
| * eda94fc74b spi: s3c64xx: fix timeout counters in flush_fifo
| * e3b57186f4 spi: spi-imx: Fix pm_runtime_set_suspended() with runtime pm enabled
| * 53b1999cfd ext4: fix i_data_sem unlock order in ext4_ind_migrate()
| * b0cb4561fc ext4: avoid use-after-free in ext4_ext_show_leaf()
| * a34416ec26 ext4: ext4_search_dir should return a proper error
| * e82df17e5f of/irq: Refer to actual buffer size in of_irq_parse_one()
| * 29f3889457 drm/amd/pm: ensure the fw_info is not null before using it
| * 0a377fcace drm/radeon/r100: Handle unknown family in r100_cp_init_microcode()
| * aec72bfbc1 scsi: aacraid: Rearrange order of struct aac_srb_unit
| * cf387300b8 drm/printer: Allow NULL data in devcoredump printer
| * f921335123 drm/amd/display: Initialize get_bytes_per_element's default to 1
| * 7ab69af56a drm/amd/display: Fix index out of bounds in DCN30 color transformation
| * c130a3c09e drm/amd/display: Fix index out of bounds in degamma hardware format translation
| * ad89f83343 drm/amd/display: Fix index out of bounds in DCN30 degamma hardware format translation
| * 0167d570f6 drm/amd/display: Check stream before comparing them
| * e2743d0a03 platform/x86: touchscreen_dmi: add nanote-next quirk
| * 831e8a816e drm/amdgpu: enable gfxoff quirk on HP 705G4
| * e407715e7a drm/amdgpu: add raven1 gfxoff quirk
| * 8b1dcf25c2 jfs: Fix uninit-value access of new_ea in ea_buffer
| * 2451e5917c jfs: check if leafidx greater than num leaves per dmap tree
| * fd026b6b67 jfs: Fix uaf in dbFreeBits
| * f9db7bb112 jfs: UBSAN: shift-out-of-bounds in dbFindBits
| * 8d54001f8d drm/amd/display: Check null pointers before using dc->clk_mgr
| * 49ded70954 ata: sata_sil: Rename sil_blacklist to sil_quirks
* | b48eba851f Merge 1ebfa66638 ("drm/amd/display: Add null check for top_pipe_to_program in commit_planes_for_stream") into android12-5.10-lts
|\|
| * 1ebfa66638 drm/amd/display: Add null check for top_pipe_to_program in commit_planes_for_stream
| * de9e7f6876 iommu/vt-d: Fix potential lockup if qi_submit_sync called with 0 count
| * 5652c448da iommu/vt-d: Always reserve a domain ID for identity setup
| * 9e493f002d power: reset: brcmstb: Do not go into infinite loop if reset fails
| * d9245b9296 iommu/arm-smmu-qcom: hide last LPASS SMMU context bank from linux
| * ceff6f5e71 rcuscale: Provide clear error when async specified without primitives
| * e6897e299f fbdev: pxafb: Fix possible use after free in pxafb_task()
| * 79681036a3 x86/syscall: Avoid memcpy() for ia32 syscall_get_arguments()
| * 98111af338 ALSA: hdsp: Break infinite MIDI input flush loop
| * 219587bca2 ALSA: asihpi: Fix potential OOB array access
| * e2b200c502 ALSA: usb-audio: Add logitech Audio profile quirk
| * 35733d1a60 ALSA: usb-audio: Define macros for quirk table entries
| * 6ee6835f82 signal: Replace BUG_ON()s
| * aa4e9056df nfp: Use IRQF_NO_AUTOEN flag in request_irq()
| * 1756918f51 wifi: mwifiex: Fix memcpy() field-spanning write warning in mwifiex_cmd_802_11_scan_ext()
| * 47be40b698 proc: add config & param to block forcing mem writes
| * 02c1725eb2 ACPICA: iasl: handle empty connection_node
| * 95a91802e4 tcp: avoid reusing FIN_WAIT2 when trying to find port in connect() process
| * b7cbdd6b1b net: atlantic: Avoid warning about potential string truncation
| * a479b653d5 ipv4: Mask upper DSCP bits and ECN bits in NETLINK_FIB_LOOKUP family
| * 669d337aa1 ipv4: Check !in_dev earlier for ioctl(SIOCSIFADDR).
| * bf60b4f587 net: mvpp2: Increase size of queue_name buffer
| * e2b2558971 tipc: guard against string buffer overrun
| * cbb67e245d ACPICA: check null return of ACPI_ALLOCATE_ZEROED() in acpi_db_convert_to_package()
| * a40e7a2b80 ACPI: EC: Do not release locks during operation region accesses
| * 7cd004102b wifi: rtw88: select WANT_DEV_COREDUMP
| * 0f26f26944 wifi: ath11k: fix array out-of-bound access in SoC stats
| * 1f61d50925 blk_iocost: fix more out of bound shifts
| * 62b8a46ba8 net: sched: consistently use rcu_replace_pointer() in taprio_change()
* | baa474b078 Merge 74c63fd016 ("ACPICA: Fix memory leak if acpi_ps_get_next_field() fails") into android12-5.10-lts
|\|
| * 74c63fd016 ACPICA: Fix memory leak if acpi_ps_get_next_field() fails
| * 30cd2158f2 ACPICA: Fix memory leak if acpi_ps_get_next_namepath() fails
| * 4440bac6f0 net: hisilicon: hns_mdio: fix OF node leak in probe()
| * 1245542856 net: hisilicon: hns_dsaf_mac: fix OF node leak in hns_mac_get_info()
| * ac6e862b8d net: hisilicon: hip04: fix OF node leak in probe()
| * a7f0073fcd net/xen-netback: prevent UAF in xenvif_flush_hash()
| * ed418cad83 ice: Adjust over allocation of memory in ice_sched_add_root_node() and ice_sched_add_node()
| * b02eb7c86f wifi: ath9k_htc: Use __skb_set_length() for resetting urb before resubmit
| * 2171e1d750 wifi: ath9k: fix possible integer overflow in ath9k_get_et_stats()
| * 000bab8753 f2fs: Require FMODE_WRITE for atomic write ioctls
| * b820cb910f ALSA: hda/conexant: Fix conflicting quirk for System76 Pangolin
| * f7785c4498 ALSA: hda/generic: Unconditionally prefer preferred_dacs pairs
| * f10d29b108 ALSA: hda/realtek: Fix the push button function for the ALC257
| * 7c93044298 ALSA: mixer_oss: Remove some incorrect kfree_const() usages
| * f13b04cf65 Bluetooth: L2CAP: Fix not validating setsockopt user input
| * 4ec4641df5 media: usbtv: Remove useless locks in usbtv_video_free()
| * a73d996436 i2c: xiic: Try re-initialization on bus busy timeout
| * c9668503e4 i2c: xiic: improve error message when transfer fails to start
| * 2d320d9de7 i2c: xiic: xiic_xfer(): Fix runtime PM leak on error path
| * 148fdc3c7d i2c: xiic: Fix RX IRQ busy check
| * 30def367fa i2c: xiic: Switch from waitqueue to completion
| * 6da4bbeb16 i2c: xiic: Fix broken locking on tx_msg
| * dd70c8a89e sctp: set sk_state back to CLOSED if autobind fails in sctp_listen_start
| * 16b66c46b6 ipv4: ip_gre: Fix drops of small packets in ipgre_xmit
| * 566a931a14 net: add more sanity checks to qdisc_pkt_len_init()
| * ba26060a29 net: avoid potential underflow in qdisc_pkt_len_init() with UFO
| * 185df15984 net: ethernet: lantiq_etop: fix memory disclosure
| * c8bb4e2d5f Bluetooth: btmrvl: Use IRQF_NO_AUTOEN flag in request_irq()
* | 6f91c0260d Merge 531754952f ("netfilter: nf_tables: prevent nf_skb_duplicated corruption") into android12-5.10-lts
|\|
| * 531754952f netfilter: nf_tables: prevent nf_skb_duplicated corruption
| * 7675fe977b net: ieee802154: mcr20a: Use IRQF_NO_AUTOEN flag in request_irq()
| * 181fbbdd46 netfilter: uapi: NFTA_FLOWTABLE_HOOK is NLA_NESTED
* | 6136b834d6 Merge cdd86fb75f ("net/mlx5: Added cond_resched() to crdump collection") into android12-5.10-lts
|\|
| * cdd86fb75f net/mlx5: Added cond_resched() to crdump collection
| * ca36d6c1a4 net/mlx5: Fix error path in multi-packet WQE transmit
| * fd7fcd802e ieee802154: Fix build error
| * f55e003d26 ceph: remove the incorrect Fw reference check when dirtying pages
| * 32ee78823d mailbox: bcm2835: Fix timeout during suspend mode
| * 3948c73c92 mailbox: rockchip: fix a typo in module autoloading
| * 6b9a551b83 spi: lpspi: Simplify some error message
| * 767b71f292 usb: yurex: Fix inconsistent locking bug in yurex_read()
| * 9f1c4edee8 i2c: isch: Add missed 'else'
| * 742a1b69c0 i2c: aspeed: Update the stop sw state when the bus recovery occurs
| * 52f7cab290 mm: only enforce minimum stack gap size if it's sensible
| * 1472dd897f lockdep: fix deadlock issue between lockdep and rcu
| * 118a0c3e55 xhci: Set quirky xHC PCI hosts to D3 _after_ stopping and freeing them.
| * cc1de44135 usb: renesas-xhci: Remove renesas_xhci_pci_exit()
| * 0f8e74a061 pps: add an error check in parport_attach
| * 0e50834814 pps: remove usage of the deprecated ida_simple_xx() API
| * 47a632e5c6 USB: misc: yurex: fix race between read and write
| * bf509ca62f usb: yurex: Replace snprintf() with the safer scnprintf() variant
| * 6ea76e19d6 soc: versatile: realview: fix soc_dev leak during device remove
| * 6b3b25311d soc: versatile: realview: fix memory leak during device remove
| * 263d04df06 PCI: xilinx-nwl: Fix off-by-one in INTx IRQ handler
| * 6dacc0b667 PCI: xilinx-nwl: Use irq_data_get_irq_chip_data()
| * 46c4079460 padata: use integer wrap around to prevent deadlock on seq_nr overflow
| * 7d0079d644 nfs: fix memory leak in error path of nfs4_do_reclaim
| * a239ff33c5 fs: Fix file_set_fowner LSM hook inconsistencies
| * 47a68c7505 vfs: fix race between evice_inodes() and find_inode()&iput()
| * 0eecd2ee23 arm64: dts: rockchip: Correct the Pinebook Pro battery design capacity
| * b2cb101b9b arm64: dts: rockchip: Raise Pinebook Pro's panel backlight PWM frequency
| * d41d665346 hwrng: cctrng - Add missing clk_disable_unprepare in cctrng_resume
| * 1b2137f6c9 hwrng: mtk - Use devm_pm_runtime_enable
| * cef1056ee6 f2fs: avoid potential int overflow in sanity_check_area_boundary()
| * 85c2f7bd57 f2fs: prevent possible int overflow in dir_block_index()
| * 5e3a031dfa debugobjects: Fix conditions in fill_pool()
| * dbffe7be55 wifi: rtw88: 8822c: Fix reported RX band width
| * 44f1816749 perf/x86/intel/pt: Fix sampling synchronization
| * f76b69ab9c efistub/tpm: Use ACPI reclaim memory for event log to avoid corruption
| * 45a765f4ba ACPI: resource: Add another DMI match for the TongFang GMxXGxx
| * 4b081991c4 ACPI: sysfs: validate return type of _STR method
| * 2e20b69b86 drbd: Add NULL check for net_conf to prevent dereference in state validation
| * 62720f2daa drbd: Fix atomicity violation in drbd_uuid_set_bm()
| * a295fa38dc crypto: ccp - Properly unregister /dev/sev on sev PLATFORM_STATUS failure
| * c7148bf45d tty: rp2: Fix reset with non forgiving PCIe host bridges
| * c30558e6c5 firmware_loader: Block path traversal
| * e89f925093 bus: integrator-lm: fix OF node leak in probe()
| * 61c12c72b1 USB: class: CDC-ACM: fix race between get_serial and set_serial
| * fa83e1df43 USB: misc: cypress_cy7c63: check for short transfer
| * 1c5cd41b4b USB: appledisplay: close race between probe and completion handler
| * 84f4d44703 drm/amd/display: Round calculated vtotal
| * 60aadf84be Input: i8042 - add another board name for TUXEDO Stellaris Gen5 AMD line
| * 13175be789 Input: i8042 - add TUXEDO Stellaris 15 Slim Gen6 AMD to i8042 quirk table
| * bf3f1affba Input: i8042 - add TUXEDO Stellaris 16 Gen5 AMD to i8042 quirk table
| * cb9897b946 soc: versatile: integrator: fix OF node leak in probe() error path
| * c55ebcb216 ASoC: rt5682: Return devm_of_clk_add_hw_provider to transfer the error
| * aaaf3cd0a7 Remove *.orig pattern from .gitignore
| * f291dc4cbc mptcp: fix sometimes-uninitialized warning
| * 2dbc4b7bac selinux,smack: don't bypass permissions check in inode_setsecctx hook
| * 4b81a9f92b bpf: Fix DEVMAP_HASH overflow check on 32-bit arches
| * 0e6378dd9b Revert "bpf: Eliminate rlimit-based memory accounting for devmap maps"
| * bfe249c151 Revert "bpf: Fix DEVMAP_HASH overflow check on 32-bit arches"
| * 8926201447 x86/mm: Switch to new Intel CPU model defines
| * 089aece01a powercap: RAPL: fix invalid initialization for pl4_supported field
| * ba624f656a Input: goodix - use the new soc_intel_is_byt() helper
| * 50460579fe drm/amd/display: Fix Synaptics Cascaded Panamera DSC Determination
* | f6317d304f Merge e0dbda9f26 ("netfilter: ctnetlink: compile ctnetlink_label_size with CONFIG_NF_CONNTRACK_EVENTS") into android12-5.10-lts
|\|
| * e0dbda9f26 netfilter: ctnetlink: compile ctnetlink_label_size with CONFIG_NF_CONNTRACK_EVENTS
* | 0e233e78a8 Merge db9c5f08ee ("netfilter: nf_tables: Keep deleted flowtable hooks until after RCU") into android12-5.10-lts
|\|
| * db9c5f08ee netfilter: nf_tables: Keep deleted flowtable hooks until after RCU
* | e45c637aa5 Merge 6c36857fe5 ("net: qrtr: Update packets cloning when broadcasting") into android12-5.10-lts
|\|
| * 6c36857fe5 net: qrtr: Update packets cloning when broadcasting
* | 8acb711851 Merge ec31cf42fc ("tcp: check skb is non-NULL in tcp_rto_delta_us()") into android12-5.10-lts
|\|
| * ec31cf42fc tcp: check skb is non-NULL in tcp_rto_delta_us()
* | 69acbd5795 Merge 98d14164c0 ("net: ipv6: select DST_CACHE from IPV6_RPL_LWTUNNEL") into android12-5.10-lts
|\|
| * 98d14164c0 net: ipv6: select DST_CACHE from IPV6_RPL_LWTUNNEL
* | 13cdfa4cd3 Merge 338a0582b2 ("net: seeq: Fix use after free vulnerability in ether3 Driver Due to Race Condition") into android12-5.10-lts
|\|
| * 338a0582b2 net: seeq: Fix use after free vulnerability in ether3 Driver Due to Race Condition
* | 973b583bb8 Merge dcf48ab3ca ("netfilter: nf_reject_ipv6: fix nf_reject_ip6_tcphdr_put()") into android12-5.10-lts
|\|
| * dcf48ab3ca netfilter: nf_reject_ipv6: fix nf_reject_ip6_tcphdr_put()
* | 0407f5e40e Merge 1429a9260f ("Revert "dm: requeue IO if mapping table not yet available"") into android12-5.10-lts
|\|
| * 1429a9260f Revert "dm: requeue IO if mapping table not yet available"
| * 0c170b1e91 vhost_vdpa: assign irq bypass producer token correctly
| * 10348fb6fe vdpa: Add eventfd for the vdpa callback
| * 64add9aaf7 interconnect: qcom: sm8250: Enable sync_state
| * d4951dd16e coresight: tmc: sg: Do not leak sg_table
| * cf0674248d iio: adc: ad7606: fix standby gpio state to match the documentation
| * 1ea56cd9a6 iio: adc: ad7606: fix oversampling gpio array
| * b7413dbc63 spi: spi-fsl-lpspi: Undo runtime PM changes at driver exit time
| * 0ed35dd547 spi: lpspi: release requested DMA channels
| * d612d419a5 spi: lpspi: Silence error message upon deferred probe
| * 6f2eeba7c3 f2fs: reduce expensive checkpoint trigger frequency
| * 3dc483f0e6 f2fs: remove unneeded check condition in __f2fs_setxattr()
| * 87aceb1ce6 f2fs: fix to update i_ctime in __f2fs_setxattr()
| * db930da947 f2fs: fix typo
| * 7b5476f061 f2fs: enhance to update i_mode and acl atomically in f2fs_setattr()
| * 0f1d007bbe nfsd: return -EINVAL when namelen is 0
| * 9f03f0016f nfsd: call cache_put if xdr_reserve_space returns NULL
| * 40d5787354 ntb_perf: Fix printk format
| * ef7e34237e ntb: intel: Fix the NULL vs IS_ERR() bug for debugfs_create_dir()
| * dd598ac57d RDMA/cxgb4: Added NULL check for lookup_atid
| * 502dac909e riscv: Fix fp alignment bug in perf_callchain_user()
| * 322911a2e6 RDMA/hns: Optimize hem allocation performance
| * 07f0f643d7 RDMA/hns: Fix spin_unlock_irqrestore() called with IRQs enabled
| * 1e6195dc7a RDMA/hns: Fix the overflow risk of hem_list_calc_ba_range()
| * 6258c4fb8d RDMA/hns: Refactor root BT allocation for MTR
| * d06fbe0b87 RDMA/hns: Add mapped page count checking for MTR
| * 2a2894e90c watchdog: imx_sc_wdt: Don't disable WDT in suspend
| * 5353f8ec59 pinctrl: mvebu: Fix devinit_dove_pinctrl_probe function
| * d8c75b8a91 pinctrl: mvebu: Use devm_platform_get_and_ioremap_resource()
| * 6007359143 nfsd: fix refcount leak when file is unhashed after being found
| * 6fcb4bbbec nfsd: remove unneeded EEXIST error check in nfsd_do_file_acquire
* | da5751e56b Revert "device property: Add const qualifier to device_get_match_data() parameter"
* | 47d930c2c7 Revert "i2c: Add i2c_get_match_data()"
* | a99e4337ab Revert "hwmon: (max16065) Remove use of i2c_match_id()"
* | 9c428fa328 Revert "hwmon: (max16065) Fix alarm attributes"
* | 246d613c20 Revert "cgroup: Make operations on the cgroup root_list RCU safe"
* | 77867ef6d4 Revert "cgroup: Move rcu_head up near the top of cgroup_root"
* | 8cf71990ea Revert "inet: inet_defrag: prevent sk release while still in use"
* | 3ce4532099 Revert "bareudp: Pull inner IP header in bareudp_udp_encap_recv()."
* | 9e57ad4546 Merge 0ce9d89343 ("clk: ti: dra7-atl: Fix leak of of_nodes") into android12-5.10-lts
|\|
| * 0ce9d89343 clk: ti: dra7-atl: Fix leak of of_nodes
| * 88ba7cd9f4 pinctrl: single: fix missing error code in pcs_probe()
| * 2efe8da2dd RDMA/iwcm: Fix WARNING:at_kernel/workqueue.c:#check_flush_dependency
| * f29951897a PCI: xilinx-nwl: Fix register misspelling
| * c289903b7a PCI: keystone: Fix if-statement expression in ks_pcie_quirk()
| * badbd736e6 drivers: media: dvb-frontends/rtl2830: fix an out-of-bounds write error
| * 6ae3b9aee4 drivers: media: dvb-frontends/rtl2832: fix an out-of-bounds write error
| * 45f826f6c8 clk: rockchip: Set parent rate for DCLK_VOP clock on RK3228
| * ca34aa3782 clk: imx: imx8mp: fix clock tree update of TF-A managed clocks
| * 11396ba4f8 perf time-utils: Fix 32-bit nsec parsing
| * a10a7d6d17 perf sched timehist: Fixed timestamp error when unable to confirm event sched_in time
| * 727660723e perf sched timehist: Fix missing free of session in perf_sched__timehist()
| * ea837ae511 bpf: Fix bpf_strtol and bpf_strtoul helpers for 32bit
| * d20674f316 nilfs2: fix potential oob read in nilfs_btree_check_delete()
| * e2290906bb nilfs2: determine empty node blocks as corrupted
| * 24bf40740a nilfs2: fix potential null-ptr-deref in nilfs_btree_insert()
| * 7fc22c3b3f ext4: avoid OOB when system.data xattr changes underneath the filesystem
| * e65f698736 ext4: return error on ext4_find_inline_entry
| * 2e073a579f ext4: avoid negative min_clusters in find_group_orlov()
| * 22d591d916 ext4: avoid potential buffer_head leak in __ext4_new_inode()
| * 08c63b7962 ext4: avoid buffer_head leak in ext4_mark_inode_used()
| * c4227a38ab smackfs: Use rcu_assign_pointer() to ensure safe assignment in smk_set_cipso
| * 4766ba108b ext4: clear EXT4_GROUP_INFO_WAS_TRIMMED_BIT even mount with discard
| * 3a1a31a38f kthread: fix task state in kthread worker if being frozen
| * 28fbbd0ce7 kthread: add kthread_work tracepoints
| * dd417529c0 xz: cleanup CRC32 edits from 2018
| * 4e1c8c12ca selftests/bpf: Fix C++ compile error from missing _Bool type
| * 9374068b36 selftests/bpf: Fix error compiling test_lru_map.c
| * c2db6acd8c selftests/bpf: Fix errors compiling cg_storage_multi.h with musl libc
| * 3467a94126 selftests/bpf: Fix compiling tcp_rtt.c with musl-libc
| * 5ad69f5a04 selftests/bpf: Fix compiling flow_dissector.c with musl-libc
| * 306efef84b selftests/bpf: Fix compiling kfree_skb.c with musl-libc
| * cc52d5282a selftests/bpf: Fix missing ARRAY_SIZE() definition in bench.c
| * 04eb60af43 selftests/bpf: Fix compile error from rlim_t in sk_storage_map.c
| * 2c9b228938 tpm: Clean up TPM space after command failure
| * a0a8b7bebe xen/swiotlb: add alignment check for dma buffers
| * cb9134aa09 xen: use correct end address of kernel for conflict checking
| * 86da3c79b8 drivers:drm:exynos_drm_gsc:Fix wrong assignment in gsc_bind()
| * 02657ced60 drm/msm: fix %s null argument error
| * 72fa5f700e ipmi: docs: don't advertise deprecated sysfs entries
| * e9e482e1e5 drm/msm/a5xx: workaround early ring-buffer emptiness check
| * d041301f30 drm/msm: Drop priv->lastctx
| * 9dffbbd7b8 drm/msm: Add priv->mm_lock to protect active/inactive lists
| * 5ce4075dde drm/msm/a5xx: fix races in preemption evaluation stage
| * cfca8b26a9 drm/msm/a5xx: properly clear preemption records on resume
| * fe93cd6635 drm/msm/a5xx: disable preemption in submits by default
| * 14531e3b82 drm/msm: Fix incorrect file name output in adreno_request_fw()
| * 0338e66cba jfs: fix out-of-bounds in dbNextAG() and diAlloc()
| * 36820265a0 drm/radeon/evergreen_cs: fix int overflow errors in cs track offsets
| * 6b38aedfdc drm/rockchip: dw_hdmi: Fix reading EDID when using a forced mode
| * 5f37e8c415 drm/rockchip: vop: Allow 4096px width scaling
| * e37fead06a drm/radeon: properly handle vbios fake edid sizing
| * af2fb608e9 drm/radeon: Replace one-element array with flexible-array member
| * 5f943045ec drm/amdgpu: properly handle vbios fake edid sizing
| * cd88105616 drm/amdgpu: Replace one-element array with flexible-array member
| * 65f9be0348 drm/stm: Fix an error handling path in stm_drm_platform_probe()
| * 278ec25952 mtd: powernv: Add check devm_kasprintf() returned value
| * 7661e90a60 fbdev: hpfb: Fix an error handling path in hpfb_dio_probe()
| * 9fb482fdf3 power: supply: max17042_battery: Fix SOC threshold calc w/ no current sense
| * 5c1997f7ad power: supply: axp20x_battery: Remove design from min and max voltage
| * c59f57f3f1 power: supply: axp20x_battery: allow disabling battery charging
| * 8202306e9f hwmon: (ntc_thermistor) fix module autoloading
| * 9efa58381a mtd: slram: insert break after errors in parsing the map
| * d7a7dd2966 hwmon: (max16065) Fix alarm attributes
| * 932559f25a hwmon: (max16065) Remove use of i2c_match_id()
| * 514a1508c3 i2c: Add i2c_get_match_data()
| * 167e4371ef device property: Add const qualifier to device_get_match_data() parameter
| * aeed49dd2b hwmon: (max16065) Fix overflows seen when writing limits
| * cdb20b703e m68k: Fix kernel_clone_args.flags in m68k_clone()
| * 42a9899e56 clocksource/drivers/qcom: Add missing iounmap() on errors in msm_dt_timer_init()
| * 97586fbd69 reset: berlin: fix OF node leak in probe() error path
| * ad0b53e4b5 ARM: versatile: fix OF node leak in CPUs prepare
| * 06ceed8eff ARM: dts: imx7d-zii-rmu2: fix Ethernet PHY pinctrl property
| * fb3cd974fb ARM: dts: microchip: sam9x60: Fix rtc/rtt clocks
| * 5f19060ab0 spi: ppc4xx: Avoid returning 0 when failed to parse and map IRQ
| * 63c7417ad4 spi: ppc4xx: handle irq_of_parse_and_map() errors
| * 4bc4272e25 block: fix potential invalid pointer dereference in blk_add_partition
| * 4d2760df0f block: print symbolic error name instead of error code
| * 3630a18846 block, bfq: don't break merge chain in bfq_split_bfqq()
| * a819a496d2 block, bfq: choose the last bfqq from merge chain in bfq_setup_cooperator()
| * e1277ae780 block, bfq: fix possible UAF for bfqq->bic with merge chain
| * cab9ff7fe8 net: tipc: avoid possible garbage value
| * 207503742c net: ipv6: rpl_iptunnel: Fix memory leak in rpl_input
| * 3df68f37c5 r8169: disable ALDPS per default for RTL8125
| * 5755eabda2 net: enetc: Use IRQF_NO_AUTOEN flag in request_irq()
| * e296245ca8 bareudp: Pull inner IP header on xmit.
| * b48fae6788 geneve: Fix incorrect inner network header offset when innerprotoinherit is set
| * 3b84799ea2 net: geneve: support IPv4/IPv6 as inner protocol
| * 76851c70a5 bareudp: Pull inner IP header in bareudp_udp_encap_recv().
| * cb1f7ef1f4 bareudp: allow redirecting bareudp packets to eth devices
| * 2accdb38c1 Bluetooth: btusb: Fix not handling ZPL/short-transfer
| * 3703e18a91 can: m_can: m_can_close(): stop clocks after device has been shut down
| * b6dce5b5a8 can: m_can: Add support for transceiver as phy
| * 5cc00913c1 can: bcm: Clear bo->bcm_proc_read after remove_proc_entry().
| * 1a11a1a532 sock_map: Add a cond_resched() in sock_hash_free()
| * 557418e170 wifi: wilc1000: fix potential RCU dereference issue in wilc_parse_join_bss_param
| * f232916fab wifi: mac80211: use two-phase skb reclamation in ieee80211_do_stop()
| * 793e01f996 wifi: cfg80211: fix two more possible UBSAN-detected off-by-one errors
| * f8e0ca3049 wifi: mt76: mt7915: fix rx filter setting for bfee functionality
| * 635ccdcd98 wifi: cfg80211: fix UBSAN noise in cfg80211_wext_siwscan()
| * 7b0724f7a9 cpufreq: ti-cpufreq: Introduce quirks to handle syscon fails appropriately
| * 0723ddb2d1 netfilter: nf_tables: reject expiration higher than timeout
| * 39c1012f5f netfilter: nf_tables: reject element expiration with no timeout
| * 1c0c097ded netfilter: nf_tables: elements with timeout below CONFIG_HZ never expire
| * 4bb459040d can: j1939: use correct function name in comment
| * b0947eca2a padata: Honor the caller's alignment in case of chunk_size 0
| * 60da25076f mount: handle OOM on mnt_warn_timestamp_expiry
| * f0a39ba6e0 fs/namespace: fnic: Switch to use %ptTd
| * 396e9c5cbf mount: warn only once about timestamp range expiration
| * 9722aa53fa fs: explicitly unregister per-superblock BDIs
| * 005dcd6bc4 ACPI: PMIC: Remove unneeded check in tps68470_pmic_opregion_probe()
| * a0c1e2da65 wifi: rtw88: always wait for both firmware loading attempts
| * 16e0ab9ed3 USB: usbtmc: prevent kernel-usb-infoleak
| * 50cff34dee USB: serial: pl2303: add device id for Macrosilicon MS3020
| * 31292316c5 usb: dwc3: Fix a typo in field name
| * 4abf184168 cgroup: Move rcu_head up near the top of cgroup_root
| * 65fd90e354 gpiolib: cdev: Ignore reconfiguration without direction
| * 1880a324af ftrace: Fix possible use-after-free issue in ftrace_location()
| * e6be2e1ebc x86/ibt,ftrace: Search for __fentry__ location
| * 9705f447bf inet: inet_defrag: prevent sk release while still in use
| * 0e7814b028 mptcp: pm: Fix uaf in __timer_delete_sync
| * 6a53e5def7 mptcp: validate 'id' when stopping the ADD_ADDR retransmit timer
| * a684b45a77 mptcp: export lookup_anno_list_by_saddr
| * 9d682e89c4 gpio: prevent potential speculation leaks in gpio_device_get_desc()
| * 5badd0ae8b netfilter: nf_tables: missing iterator type in lookup walk
| * ff89db14c6 netfilter: nft_set_pipapo: walk over current view on netlink dump
| * 45a81667e0 cgroup: Make operations on the cgroup root_list RCU safe
| * 57a3d89831 ocfs2: strict bound check before memcmp in ocfs2_xattr_find_entry()
| * 34759b7e44 ocfs2: add bounds checking to ocfs2_xattr_find_entry()
| * d5624db232 x86/hyperv: Set X86_FEATURE_TSC_KNOWN_FREQ when Hyper-V provides frequency
| * bbd11db41b spi: bcm63xx: Enable module autoloading
| * 040511d9f2 drm: komeda: Fix an issue related to normalized zpos
| * 3d39061b7b ASoC: tda7419: fix module autoloading
| * 9c6d4649f2 ASoC: intel: fix module autoloading
| * de46b1d24f wifi: iwlwifi: mvm: don't wait for tx queues if firmware is dead
| * db81677f4b wifi: iwlwifi: lower message level for FW buffer destination
| * dd34ef88d5 net: ftgmac100: Ensure tx descriptor updates are visible
| * 1a8e85289e microblaze: don't treat zero reserved memory regions as error
| * 2e5052143c pinctrl: at91: make it work with current gpiolib
| * 9f08d024ed ALSA: hda/realtek - FIxed ALC285 headphone no sound
| * fff183aa3c ALSA: hda/realtek - Fixed ALC256 headphone no sound
| * 0f4da063a1 ASoC: allow module autoloading for table db1200_pids
| * 007180fcb6 dma-buf: heaps: Fix off-by-one in CMA heap fault handler
| * 5a2cc2bb81 ASoC: meson: axg-card: fix 'use-after-free'
* | 1f05cd743b Merge ae96b02b9d ("soundwire: stream: Revert "soundwire: stream: fix programming slave ports for non-continous port maps"") into android12-5.10-lts
|\|
| * ae96b02b9d soundwire: stream: Revert "soundwire: stream: fix programming slave ports for non-continous port maps"
| * 609260542c spi: nxp-fspi: fix the KASAN report out-of-bounds bug
| * 1f31f51bfc net: dpaa: Pad packets to ETH_ZLEN
| * f2b13ec208 net: ftgmac100: Enable TX interrupt to avoid TX timeout
| * 392f6a97fc fou: fix initialization of grc
| * b9063702a0 net/mlx5e: Add missing link modes to ptys2ethtool_map
| * 097cc80396 net/mlx5: Update the list of the PCI supported devices
| * e7a9cca35e ice: fix accounting for filters shared by multiple VSIs
| * a38c552abf hwmon: (pmbus) Conditionally clear individual status bits for pmbus rev >= 1.2
| * b9d15b50b2 hwmon: (pmbus) Introduce and use write_byte_data callback
| * e829dbaf76 minmax: reduce min/max macro expansion in atomisp driver
| * 78078862f0 arm64: dts: rockchip: override BIOS_DISABLE signal via GPIO hog on RK3399 Puma
| * 14341f28a3 NFS: Avoid unnecessary rescanning of the per-server delegation list
| * 4f0e491644 Input: i8042 - add Fujitsu Lifebook E756 to i8042 quirk table
| * 00d54431b0 drm/msm/adreno: Fix error return if missing firmware-name
| * 44eb665889 scripts: kconfig: merge_config: config files: add a trailing newline
| * 9deecde637 Input: synaptics - enable SMBus for HP Elitebook 840 G2
| * 3f7183e28f Input: ads7846 - ratelimit the spi_sync error message
| * f08589057f btrfs: update target inode's ctime on unlink
| * 43662ba6ca powerpc/mm: Fix boot warning with hugepages and CONFIG_DEBUG_VIRTUAL
| * acb4baa484 net: phy: vitesse: repair vsc73xx autonegotiation
| * 3b9ca02300 net: ethernet: use ip_hdrlen() instead of bit shift
| * b06bb81ef9 usbnet: ipheth: fix carrier detection in modes 1 and 4
* | 8441327954 Revert "Merge 8a30bee7f5 ("usb: dwc3: core: update LC timer as per USB Spec V3.2") into android12-5.10-lts"
* | 18eef3d55a Merge 8a30bee7f5 ("usb: dwc3: core: update LC timer as per USB Spec V3.2") into android12-5.10-lts
|\|
| * 8a30bee7f5 usb: dwc3: core: update LC timer as per USB Spec V3.2
| * 1ac0667658 usb: dwc3: core: Enable GUCTL1 bit 10 for fixing termination error after resume bug
| * 6b3af2ad53 usb: dwc3: Decouple USB 2.0 L1 & L2 events
* e477d44e5f Merge branch 'android12-5.10' into android12-5.10-lts

Change-Id: I8bacb03dd3c3b30729c5ec54bdd57a03f6ff7fe1
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2024-11-28 17:25:42 +00:00

4674 lines
110 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* fs/f2fs/file.c
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*/
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include <linux/stat.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/falloc.h>
#include <linux/types.h>
#include <linux/compat.h>
#include <linux/uaccess.h>
#include <linux/mount.h>
#include <linux/pagevec.h>
#include <linux/uio.h>
#include <linux/uuid.h>
#include <linux/file.h>
#include <linux/nls.h>
#include <linux/sched/signal.h>
#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include "xattr.h"
#include "acl.h"
#include "gc.h"
#include <trace/events/f2fs.h>
#include <uapi/linux/f2fs.h>
static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
{
struct inode *inode = file_inode(vmf->vma->vm_file);
vm_fault_t ret;
f2fs_down_read(&F2FS_I(inode)->i_mmap_sem);
ret = filemap_fault(vmf);
f2fs_up_read(&F2FS_I(inode)->i_mmap_sem);
if (ret & VM_FAULT_LOCKED)
f2fs_update_iostat(F2FS_I_SB(inode), APP_MAPPED_READ_IO,
F2FS_BLKSIZE);
trace_f2fs_filemap_fault(inode, vmf->pgoff, (unsigned long)ret);
return ret;
}
static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
{
struct page *page = vmf->page;
struct inode *inode = file_inode(vmf->vma->vm_file);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
bool need_alloc = true;
int err = 0;
if (unlikely(IS_IMMUTABLE(inode)))
return VM_FAULT_SIGBUS;
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED))
return VM_FAULT_SIGBUS;
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto err;
}
if (!f2fs_is_checkpoint_ready(sbi)) {
err = -ENOSPC;
goto err;
}
err = f2fs_convert_inline_inode(inode);
if (err)
goto err;
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (f2fs_compressed_file(inode)) {
int ret = f2fs_is_compressed_cluster(inode, page->index);
if (ret < 0) {
err = ret;
goto err;
} else if (ret) {
need_alloc = false;
}
}
#endif
/* should do out of any locked page */
if (need_alloc)
f2fs_balance_fs(sbi, true);
sb_start_pagefault(inode->i_sb);
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
file_update_time(vmf->vma->vm_file);
f2fs_down_read(&F2FS_I(inode)->i_mmap_sem);
lock_page(page);
if (unlikely(page->mapping != inode->i_mapping ||
page_offset(page) > i_size_read(inode) ||
!PageUptodate(page))) {
unlock_page(page);
err = -EFAULT;
goto out_sem;
}
if (need_alloc) {
/* block allocation */
f2fs_do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true);
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_get_block(&dn, page->index);
f2fs_do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false);
}
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (!need_alloc) {
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
f2fs_put_dnode(&dn);
}
#endif
if (err) {
unlock_page(page);
goto out_sem;
}
f2fs_wait_on_page_writeback(page, DATA, false, true);
/* wait for GCed page writeback via META_MAPPING */
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
/*
* check to see if the page is mapped already (no holes)
*/
if (PageMappedToDisk(page))
goto out_sem;
/* page is wholly or partially inside EOF */
if (((loff_t)(page->index + 1) << PAGE_SHIFT) >
i_size_read(inode)) {
loff_t offset;
offset = i_size_read(inode) & ~PAGE_MASK;
zero_user_segment(page, offset, PAGE_SIZE);
}
set_page_dirty(page);
if (!PageUptodate(page))
SetPageUptodate(page);
f2fs_update_iostat(sbi, APP_MAPPED_IO, F2FS_BLKSIZE);
f2fs_update_time(sbi, REQ_TIME);
trace_f2fs_vm_page_mkwrite(page, DATA);
out_sem:
f2fs_up_read(&F2FS_I(inode)->i_mmap_sem);
sb_end_pagefault(inode->i_sb);
err:
return block_page_mkwrite_return(err);
}
static const struct vm_operations_struct f2fs_file_vm_ops = {
.fault = f2fs_filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = f2fs_vm_page_mkwrite,
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
.allow_speculation = filemap_allow_speculation,
#endif
};
static int get_parent_ino(struct inode *inode, nid_t *pino)
{
struct dentry *dentry;
/*
* Make sure to get the non-deleted alias. The alias associated with
* the open file descriptor being fsync()'ed may be deleted already.
*/
dentry = d_find_alias(inode);
if (!dentry)
return 0;
*pino = parent_ino(dentry);
dput(dentry);
return 1;
}
static inline enum cp_reason_type need_do_checkpoint(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
enum cp_reason_type cp_reason = CP_NO_NEEDED;
if (!S_ISREG(inode->i_mode))
cp_reason = CP_NON_REGULAR;
else if (f2fs_compressed_file(inode))
cp_reason = CP_COMPRESSED;
else if (inode->i_nlink != 1)
cp_reason = CP_HARDLINK;
else if (is_sbi_flag_set(sbi, SBI_NEED_CP))
cp_reason = CP_SB_NEED_CP;
else if (file_wrong_pino(inode))
cp_reason = CP_WRONG_PINO;
else if (!f2fs_space_for_roll_forward(sbi))
cp_reason = CP_NO_SPC_ROLL;
else if (!f2fs_is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
cp_reason = CP_NODE_NEED_CP;
else if (test_opt(sbi, FASTBOOT))
cp_reason = CP_FASTBOOT_MODE;
else if (F2FS_OPTION(sbi).active_logs == 2)
cp_reason = CP_SPEC_LOG_NUM;
else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT &&
f2fs_need_dentry_mark(sbi, inode->i_ino) &&
f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
TRANS_DIR_INO))
cp_reason = CP_RECOVER_DIR;
else if (f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
XATTR_DIR_INO))
cp_reason = CP_XATTR_DIR;
return cp_reason;
}
static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino)
{
struct page *i = find_get_page(NODE_MAPPING(sbi), ino);
bool ret = false;
/* But we need to avoid that there are some inode updates */
if ((i && PageDirty(i)) || f2fs_need_inode_block_update(sbi, ino))
ret = true;
f2fs_put_page(i, 0);
return ret;
}
static void try_to_fix_pino(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
nid_t pino;
f2fs_down_write(&fi->i_sem);
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
get_parent_ino(inode, &pino)) {
f2fs_i_pino_write(inode, pino);
file_got_pino(inode);
}
f2fs_up_write(&fi->i_sem);
}
static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
int datasync, bool atomic)
{
struct inode *inode = file->f_mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
nid_t ino = inode->i_ino;
int ret = 0;
enum cp_reason_type cp_reason = 0;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX,
.for_reclaim = 0,
};
unsigned int seq_id = 0;
if (unlikely(f2fs_readonly(inode->i_sb)))
return 0;
trace_f2fs_sync_file_enter(inode);
if (S_ISDIR(inode->i_mode))
goto go_write;
/* if fdatasync is triggered, let's do in-place-update */
if (datasync || get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks)
set_inode_flag(inode, FI_NEED_IPU);
ret = file_write_and_wait_range(file, start, end);
clear_inode_flag(inode, FI_NEED_IPU);
if (ret || is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret);
return ret;
}
/* if the inode is dirty, let's recover all the time */
if (!f2fs_skip_inode_update(inode, datasync)) {
f2fs_write_inode(inode, NULL);
goto go_write;
}
/*
* if there is no written data, don't waste time to write recovery info.
*/
if (!is_inode_flag_set(inode, FI_APPEND_WRITE) &&
!f2fs_exist_written_data(sbi, ino, APPEND_INO)) {
/* it may call write_inode just prior to fsync */
if (need_inode_page_update(sbi, ino))
goto go_write;
if (is_inode_flag_set(inode, FI_UPDATE_WRITE) ||
f2fs_exist_written_data(sbi, ino, UPDATE_INO))
goto flush_out;
goto out;
}
go_write:
/*
* Both of fdatasync() and fsync() are able to be recovered from
* sudden-power-off.
*/
f2fs_down_read(&F2FS_I(inode)->i_sem);
cp_reason = need_do_checkpoint(inode);
f2fs_up_read(&F2FS_I(inode)->i_sem);
if (cp_reason) {
/* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1);
/*
* We've secured consistency through sync_fs. Following pino
* will be used only for fsynced inodes after checkpoint.
*/
try_to_fix_pino(inode);
clear_inode_flag(inode, FI_APPEND_WRITE);
clear_inode_flag(inode, FI_UPDATE_WRITE);
goto out;
}
sync_nodes:
atomic_inc(&sbi->wb_sync_req[NODE]);
ret = f2fs_fsync_node_pages(sbi, inode, &wbc, atomic, &seq_id);
atomic_dec(&sbi->wb_sync_req[NODE]);
if (ret)
goto out;
/* if cp_error was enabled, we should avoid infinite loop */
if (unlikely(f2fs_cp_error(sbi))) {
ret = -EIO;
goto out;
}
if (f2fs_need_inode_block_update(sbi, ino)) {
f2fs_mark_inode_dirty_sync(inode, true);
f2fs_write_inode(inode, NULL);
goto sync_nodes;
}
/*
* If it's atomic_write, it's just fine to keep write ordering. So
* here we don't need to wait for node write completion, since we use
* node chain which serializes node blocks. If one of node writes are
* reordered, we can see simply broken chain, resulting in stopping
* roll-forward recovery. It means we'll recover all or none node blocks
* given fsync mark.
*/
if (!atomic) {
ret = f2fs_wait_on_node_pages_writeback(sbi, seq_id);
if (ret)
goto out;
}
/* once recovery info is written, don't need to tack this */
f2fs_remove_ino_entry(sbi, ino, APPEND_INO);
clear_inode_flag(inode, FI_APPEND_WRITE);
flush_out:
if (!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER)
ret = f2fs_issue_flush(sbi, inode->i_ino);
if (!ret) {
f2fs_remove_ino_entry(sbi, ino, UPDATE_INO);
clear_inode_flag(inode, FI_UPDATE_WRITE);
f2fs_remove_ino_entry(sbi, ino, FLUSH_INO);
}
f2fs_update_time(sbi, REQ_TIME);
out:
trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret);
return ret;
}
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(file)))))
return -EIO;
return f2fs_do_sync_file(file, start, end, datasync, false);
}
static bool __found_offset(struct address_space *mapping,
struct dnode_of_data *dn, pgoff_t index, int whence)
{
block_t blkaddr = f2fs_data_blkaddr(dn);
struct inode *inode = mapping->host;
bool compressed_cluster = false;
if (f2fs_compressed_file(inode)) {
block_t first_blkaddr = data_blkaddr(dn->inode, dn->node_page,
ALIGN_DOWN(dn->ofs_in_node, F2FS_I(inode)->i_cluster_size));
compressed_cluster = first_blkaddr == COMPRESS_ADDR;
}
switch (whence) {
case SEEK_DATA:
if (__is_valid_data_blkaddr(blkaddr))
return true;
if (blkaddr == NEW_ADDR &&
xa_get_mark(&mapping->i_pages, index, PAGECACHE_TAG_DIRTY))
return true;
if (compressed_cluster)
return true;
break;
case SEEK_HOLE:
if (compressed_cluster)
return false;
if (blkaddr == NULL_ADDR)
return true;
break;
}
return false;
}
static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
loff_t maxbytes = inode->i_sb->s_maxbytes;
struct dnode_of_data dn;
pgoff_t pgofs, end_offset;
loff_t data_ofs = offset;
loff_t isize;
int err = 0;
inode_lock(inode);
isize = i_size_read(inode);
if (offset >= isize)
goto fail;
/* handle inline data case */
if (f2fs_has_inline_data(inode)) {
if (whence == SEEK_HOLE) {
data_ofs = isize;
goto found;
} else if (whence == SEEK_DATA) {
data_ofs = offset;
goto found;
}
}
pgofs = (pgoff_t)(offset >> PAGE_SHIFT);
for (; data_ofs < isize; data_ofs = (loff_t)pgofs << PAGE_SHIFT) {
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_get_dnode_of_data(&dn, pgofs, LOOKUP_NODE);
if (err && err != -ENOENT) {
goto fail;
} else if (err == -ENOENT) {
/* direct node does not exists */
if (whence == SEEK_DATA) {
pgofs = f2fs_get_next_page_offset(&dn, pgofs);
continue;
} else {
goto found;
}
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
/* find data/hole in dnode block */
for (; dn.ofs_in_node < end_offset;
dn.ofs_in_node++, pgofs++,
data_ofs = (loff_t)pgofs << PAGE_SHIFT) {
block_t blkaddr;
blkaddr = f2fs_data_blkaddr(&dn);
if (__is_valid_data_blkaddr(blkaddr) &&
!f2fs_is_valid_blkaddr(F2FS_I_SB(inode),
blkaddr, DATA_GENERIC_ENHANCE)) {
f2fs_put_dnode(&dn);
goto fail;
}
if (__found_offset(file->f_mapping, &dn,
pgofs, whence)) {
f2fs_put_dnode(&dn);
goto found;
}
}
f2fs_put_dnode(&dn);
}
if (whence == SEEK_DATA)
goto fail;
found:
if (whence == SEEK_HOLE && data_ofs > isize)
data_ofs = isize;
inode_unlock(inode);
return vfs_setpos(file, data_ofs, maxbytes);
fail:
inode_unlock(inode);
return -ENXIO;
}
static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
loff_t maxbytes = inode->i_sb->s_maxbytes;
if (f2fs_compressed_file(inode))
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
switch (whence) {
case SEEK_SET:
case SEEK_CUR:
case SEEK_END:
return generic_file_llseek_size(file, offset, whence,
maxbytes, i_size_read(inode));
case SEEK_DATA:
case SEEK_HOLE:
if (offset < 0)
return -ENXIO;
return f2fs_seek_block(file, offset, whence);
}
return -EINVAL;
}
static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file_inode(file);
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
return -EIO;
if (!f2fs_is_compress_backend_ready(inode))
return -EOPNOTSUPP;
file_accessed(file);
vma->vm_ops = &f2fs_file_vm_ops;
set_inode_flag(inode, FI_MMAP_FILE);
return 0;
}
static int f2fs_file_open(struct inode *inode, struct file *filp)
{
int err = fscrypt_file_open(inode, filp);
if (err)
return err;
if (!f2fs_is_compress_backend_ready(inode))
return -EOPNOTSUPP;
err = fsverity_file_open(inode, filp);
if (err)
return err;
filp->f_mode |= FMODE_NOWAIT;
return dquot_file_open(inode, filp);
}
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
struct f2fs_node *raw_node;
int nr_free = 0, ofs = dn->ofs_in_node, len = count;
__le32 *addr;
int base = 0;
bool compressed_cluster = false;
int cluster_index = 0, valid_blocks = 0;
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
bool released = !atomic_read(&F2FS_I(dn->inode)->i_compr_blocks);
if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
base = get_extra_isize(dn->inode);
raw_node = F2FS_NODE(dn->node_page);
addr = blkaddr_in_node(raw_node) + base + ofs;
/* Assumption: truncateion starts with cluster */
for (; count > 0; count--, addr++, dn->ofs_in_node++, cluster_index++) {
block_t blkaddr = le32_to_cpu(*addr);
if (f2fs_compressed_file(dn->inode) &&
!(cluster_index & (cluster_size - 1))) {
if (compressed_cluster)
f2fs_i_compr_blocks_update(dn->inode,
valid_blocks, false);
compressed_cluster = (blkaddr == COMPRESS_ADDR);
valid_blocks = 0;
}
if (blkaddr == NULL_ADDR)
continue;
dn->data_blkaddr = NULL_ADDR;
f2fs_set_data_blkaddr(dn);
if (__is_valid_data_blkaddr(blkaddr)) {
if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
DATA_GENERIC_ENHANCE))
continue;
if (compressed_cluster)
valid_blocks++;
}
if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN);
f2fs_invalidate_blocks(sbi, blkaddr);
if (!released || blkaddr != COMPRESS_ADDR)
nr_free++;
}
if (compressed_cluster)
f2fs_i_compr_blocks_update(dn->inode, valid_blocks, false);
if (nr_free) {
pgoff_t fofs;
/*
* once we invalidate valid blkaddr in range [ofs, ofs + count],
* we will invalidate all blkaddr in the whole range.
*/
fofs = f2fs_start_bidx_of_node(ofs_of_node(dn->node_page),
dn->inode) + ofs;
f2fs_update_read_extent_cache_range(dn, fofs, 0, len);
f2fs_update_age_extent_cache_range(dn, fofs, len);
dec_valid_block_count(sbi, dn->inode, nr_free);
}
dn->ofs_in_node = ofs;
f2fs_update_time(sbi, REQ_TIME);
trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
dn->ofs_in_node, nr_free);
}
void f2fs_truncate_data_blocks(struct dnode_of_data *dn)
{
f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK(dn->inode));
}
static int truncate_partial_data_page(struct inode *inode, u64 from,
bool cache_only)
{
loff_t offset = from & (PAGE_SIZE - 1);
pgoff_t index = from >> PAGE_SHIFT;
struct address_space *mapping = inode->i_mapping;
struct page *page;
if (!offset && !cache_only)
return 0;
if (cache_only) {
page = find_lock_page(mapping, index);
if (page && PageUptodate(page))
goto truncate_out;
f2fs_put_page(page, 1);
return 0;
}
page = f2fs_get_lock_data_page(inode, index, true);
if (IS_ERR(page))
return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page);
truncate_out:
f2fs_wait_on_page_writeback(page, DATA, true, true);
zero_user(page, offset, PAGE_SIZE - offset);
/* An encrypted inode should have a key and truncate the last page. */
f2fs_bug_on(F2FS_I_SB(inode), cache_only && IS_ENCRYPTED(inode));
if (!cache_only)
set_page_dirty(page);
f2fs_put_page(page, 1);
return 0;
}
int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
pgoff_t free_from;
int count = 0, err = 0;
struct page *ipage;
bool truncate_page = false;
trace_f2fs_truncate_blocks_enter(inode, from);
free_from = (pgoff_t)F2FS_BLK_ALIGN(from);
if (free_from >= max_file_blocks(inode))
goto free_partial;
if (lock)
f2fs_lock_op(sbi);
ipage = f2fs_get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage)) {
err = PTR_ERR(ipage);
goto out;
}
if (f2fs_has_inline_data(inode)) {
f2fs_truncate_inline_inode(inode, ipage, from);
f2fs_put_page(ipage, 1);
truncate_page = true;
goto out;
}
set_new_dnode(&dn, inode, ipage, NULL, 0);
err = f2fs_get_dnode_of_data(&dn, free_from, LOOKUP_NODE_RA);
if (err) {
if (err == -ENOENT)
goto free_next;
goto out;
}
count = ADDRS_PER_PAGE(dn.node_page, inode);
count -= dn.ofs_in_node;
f2fs_bug_on(sbi, count < 0);
if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
f2fs_truncate_data_blocks_range(&dn, count);
free_from += count;
}
f2fs_put_dnode(&dn);
free_next:
err = f2fs_truncate_inode_blocks(inode, free_from);
out:
if (lock)
f2fs_unlock_op(sbi);
free_partial:
/* lastly zero out the first data page */
if (!err)
err = truncate_partial_data_page(inode, from, truncate_page);
trace_f2fs_truncate_blocks_exit(inode, err);
return err;
}
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
{
u64 free_from = from;
int err;
#ifdef CONFIG_F2FS_FS_COMPRESSION
/*
* for compressed file, only support cluster size
* aligned truncation.
*/
if (f2fs_compressed_file(inode))
free_from = round_up(from,
F2FS_I(inode)->i_cluster_size << PAGE_SHIFT);
#endif
err = f2fs_do_truncate_blocks(inode, free_from, lock);
if (err)
return err;
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (from != free_from) {
err = f2fs_truncate_partial_cluster(inode, from, lock);
if (err)
return err;
}
#endif
return 0;
}
int f2fs_truncate(struct inode *inode)
{
int err;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
return -EIO;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return 0;
trace_f2fs_truncate(inode);
if (time_to_inject(F2FS_I_SB(inode), FAULT_TRUNCATE)) {
f2fs_show_injection_info(F2FS_I_SB(inode), FAULT_TRUNCATE);
return -EIO;
}
err = dquot_initialize(inode);
if (err)
return err;
/* we should check inline_data size */
if (!f2fs_may_inline_data(inode)) {
err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
err = f2fs_truncate_blocks(inode, i_size_read(inode), true);
if (err)
return err;
inode->i_mtime = inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, false);
return 0;
}
int f2fs_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags)
{
struct inode *inode = d_inode(path->dentry);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_inode *ri;
unsigned int flags;
if (f2fs_has_extra_attr(inode) &&
f2fs_sb_has_inode_crtime(F2FS_I_SB(inode)) &&
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) {
stat->result_mask |= STATX_BTIME;
stat->btime.tv_sec = fi->i_crtime.tv_sec;
stat->btime.tv_nsec = fi->i_crtime.tv_nsec;
}
flags = fi->i_flags;
if (flags & F2FS_COMPR_FL)
stat->attributes |= STATX_ATTR_COMPRESSED;
if (flags & F2FS_APPEND_FL)
stat->attributes |= STATX_ATTR_APPEND;
if (IS_ENCRYPTED(inode))
stat->attributes |= STATX_ATTR_ENCRYPTED;
if (flags & F2FS_IMMUTABLE_FL)
stat->attributes |= STATX_ATTR_IMMUTABLE;
if (flags & F2FS_NODUMP_FL)
stat->attributes |= STATX_ATTR_NODUMP;
if (IS_VERITY(inode))
stat->attributes |= STATX_ATTR_VERITY;
stat->attributes_mask |= (STATX_ATTR_COMPRESSED |
STATX_ATTR_APPEND |
STATX_ATTR_ENCRYPTED |
STATX_ATTR_IMMUTABLE |
STATX_ATTR_NODUMP |
STATX_ATTR_VERITY);
generic_fillattr(inode, stat);
/* we need to show initial sectors used for inline_data/dentries */
if ((S_ISREG(inode->i_mode) && f2fs_has_inline_data(inode)) ||
f2fs_has_inline_dentry(inode))
stat->blocks += (stat->size + 511) >> 9;
return 0;
}
#ifdef CONFIG_F2FS_FS_POSIX_ACL
static void __setattr_copy(struct inode *inode, const struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
inode->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME)
inode->i_mtime = attr->ia_mtime;
if (ia_valid & ATTR_CTIME)
inode->i_ctime = attr->ia_ctime;
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
if (!in_group_p(inode->i_gid) &&
!capable_wrt_inode_uidgid(inode, CAP_FSETID))
mode &= ~S_ISGID;
set_acl_inode(inode, mode);
}
}
#else
#define __setattr_copy setattr_copy
#endif
int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
int err;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
return -EIO;
if (unlikely(IS_IMMUTABLE(inode)))
return -EPERM;
if (unlikely(IS_APPEND(inode) &&
(attr->ia_valid & (ATTR_MODE | ATTR_UID |
ATTR_GID | ATTR_TIMES_SET))))
return -EPERM;
if ((attr->ia_valid & ATTR_SIZE) &&
!f2fs_is_compress_backend_ready(inode))
return -EOPNOTSUPP;
err = setattr_prepare(dentry, attr);
if (err)
return err;
err = fscrypt_prepare_setattr(dentry, attr);
if (err)
return err;
err = fsverity_prepare_setattr(dentry, attr);
if (err)
return err;
if (is_quota_modification(inode, attr)) {
err = dquot_initialize(inode);
if (err)
return err;
}
if ((attr->ia_valid & ATTR_UID &&
!uid_eq(attr->ia_uid, inode->i_uid)) ||
(attr->ia_valid & ATTR_GID &&
!gid_eq(attr->ia_gid, inode->i_gid))) {
f2fs_lock_op(F2FS_I_SB(inode));
err = dquot_transfer(inode, attr);
if (err) {
set_sbi_flag(F2FS_I_SB(inode),
SBI_QUOTA_NEED_REPAIR);
f2fs_unlock_op(F2FS_I_SB(inode));
return err;
}
/*
* update uid/gid under lock_op(), so that dquot and inode can
* be updated atomically.
*/
if (attr->ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
f2fs_mark_inode_dirty_sync(inode, true);
f2fs_unlock_op(F2FS_I_SB(inode));
}
if (attr->ia_valid & ATTR_SIZE) {
loff_t old_size = i_size_read(inode);
if (attr->ia_size > MAX_INLINE_DATA(inode)) {
/*
* should convert inline inode before i_size_write to
* keep smaller than inline_data size with inline flag.
*/
err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
truncate_setsize(inode, attr->ia_size);
if (attr->ia_size <= old_size)
err = f2fs_truncate(inode);
/*
* do not trim all blocks after i_size if target size is
* larger than i_size.
*/
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
if (err)
return err;
spin_lock(&F2FS_I(inode)->i_size_lock);
inode->i_mtime = inode->i_ctime = current_time(inode);
F2FS_I(inode)->last_disk_size = i_size_read(inode);
spin_unlock(&F2FS_I(inode)->i_size_lock);
}
__setattr_copy(inode, attr);
if (attr->ia_valid & ATTR_MODE) {
err = posix_acl_chmod(inode, f2fs_get_inode_mode(inode));
if (is_inode_flag_set(inode, FI_ACL_MODE)) {
if (!err)
inode->i_mode = F2FS_I(inode)->i_acl_mode;
clear_inode_flag(inode, FI_ACL_MODE);
}
}
/* file size may changed here */
f2fs_mark_inode_dirty_sync(inode, true);
/* inode change will produce dirty node pages flushed by checkpoint */
f2fs_balance_fs(F2FS_I_SB(inode), true);
return err;
}
const struct inode_operations f2fs_file_inode_operations = {
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
.get_acl = f2fs_get_acl,
.set_acl = f2fs_set_acl,
.listxattr = f2fs_listxattr,
.fiemap = f2fs_fiemap,
};
static int fill_zero(struct inode *inode, pgoff_t index,
loff_t start, loff_t len)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *page;
if (!len)
return 0;
f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
page = f2fs_get_new_data_page(inode, NULL, index, false);
f2fs_unlock_op(sbi);
if (IS_ERR(page))
return PTR_ERR(page);
f2fs_wait_on_page_writeback(page, DATA, true, true);
zero_user(page, start, len);
set_page_dirty(page);
f2fs_put_page(page, 1);
return 0;
}
int f2fs_truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
{
int err;
while (pg_start < pg_end) {
struct dnode_of_data dn;
pgoff_t end_offset, count;
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_get_dnode_of_data(&dn, pg_start, LOOKUP_NODE);
if (err) {
if (err == -ENOENT) {
pg_start = f2fs_get_next_page_offset(&dn,
pg_start);
continue;
}
return err;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
count = min(end_offset - dn.ofs_in_node, pg_end - pg_start);
f2fs_bug_on(F2FS_I_SB(inode), count == 0 || count > end_offset);
f2fs_truncate_data_blocks_range(&dn, count);
f2fs_put_dnode(&dn);
pg_start += count;
}
return 0;
}
static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
{
pgoff_t pg_start, pg_end;
loff_t off_start, off_end;
int ret;
ret = f2fs_convert_inline_inode(inode);
if (ret)
return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
off_start = offset & (PAGE_SIZE - 1);
off_end = (offset + len) & (PAGE_SIZE - 1);
if (pg_start == pg_end) {
ret = fill_zero(inode, pg_start, off_start,
off_end - off_start);
if (ret)
return ret;
} else {
if (off_start) {
ret = fill_zero(inode, pg_start++, off_start,
PAGE_SIZE - off_start);
if (ret)
return ret;
}
if (off_end) {
ret = fill_zero(inode, pg_end, 0, off_end);
if (ret)
return ret;
}
if (pg_start < pg_end) {
loff_t blk_start, blk_end;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
f2fs_balance_fs(sbi, true);
blk_start = (loff_t)pg_start << PAGE_SHIFT;
blk_end = (loff_t)pg_end << PAGE_SHIFT;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
truncate_pagecache_range(inode, blk_start, blk_end - 1);
f2fs_lock_op(sbi);
ret = f2fs_truncate_hole(inode, pg_start, pg_end);
f2fs_unlock_op(sbi);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
}
}
return ret;
}
static int __read_out_blkaddrs(struct inode *inode, block_t *blkaddr,
int *do_replace, pgoff_t off, pgoff_t len)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
int ret, done, i;
next_dnode:
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, off, LOOKUP_NODE_RA);
if (ret && ret != -ENOENT) {
return ret;
} else if (ret == -ENOENT) {
if (dn.max_level == 0)
return -ENOENT;
done = min((pgoff_t)ADDRS_PER_BLOCK(inode) -
dn.ofs_in_node, len);
blkaddr += done;
do_replace += done;
goto next;
}
done = min((pgoff_t)ADDRS_PER_PAGE(dn.node_page, inode) -
dn.ofs_in_node, len);
for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) {
*blkaddr = f2fs_data_blkaddr(&dn);
if (__is_valid_data_blkaddr(*blkaddr) &&
!f2fs_is_valid_blkaddr(sbi, *blkaddr,
DATA_GENERIC_ENHANCE)) {
f2fs_put_dnode(&dn);
return -EFSCORRUPTED;
}
if (!f2fs_is_checkpointed_data(sbi, *blkaddr)) {
if (f2fs_lfs_mode(sbi)) {
f2fs_put_dnode(&dn);
return -EOPNOTSUPP;
}
/* do not invalidate this block address */
f2fs_update_data_blkaddr(&dn, NULL_ADDR);
*do_replace = 1;
}
}
f2fs_put_dnode(&dn);
next:
len -= done;
off += done;
if (len)
goto next_dnode;
return 0;
}
static int __roll_back_blkaddrs(struct inode *inode, block_t *blkaddr,
int *do_replace, pgoff_t off, int len)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
int ret, i;
for (i = 0; i < len; i++, do_replace++, blkaddr++) {
if (*do_replace == 0)
continue;
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, off + i, LOOKUP_NODE_RA);
if (ret) {
dec_valid_block_count(sbi, inode, 1);
f2fs_invalidate_blocks(sbi, *blkaddr);
} else {
f2fs_update_data_blkaddr(&dn, *blkaddr);
}
f2fs_put_dnode(&dn);
}
return 0;
}
static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode,
block_t *blkaddr, int *do_replace,
pgoff_t src, pgoff_t dst, pgoff_t len, bool full)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(src_inode);
pgoff_t i = 0;
int ret;
while (i < len) {
if (blkaddr[i] == NULL_ADDR && !full) {
i++;
continue;
}
if (do_replace[i] || blkaddr[i] == NULL_ADDR) {
struct dnode_of_data dn;
struct node_info ni;
size_t new_size;
pgoff_t ilen;
set_new_dnode(&dn, dst_inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, dst + i, ALLOC_NODE);
if (ret)
return ret;
ret = f2fs_get_node_info(sbi, dn.nid, &ni, false);
if (ret) {
f2fs_put_dnode(&dn);
return ret;
}
ilen = min((pgoff_t)
ADDRS_PER_PAGE(dn.node_page, dst_inode) -
dn.ofs_in_node, len - i);
do {
dn.data_blkaddr = f2fs_data_blkaddr(&dn);
f2fs_truncate_data_blocks_range(&dn, 1);
if (do_replace[i]) {
f2fs_i_blocks_write(src_inode,
1, false, false);
f2fs_i_blocks_write(dst_inode,
1, true, false);
f2fs_replace_block(sbi, &dn, dn.data_blkaddr,
blkaddr[i], ni.version, true, false);
do_replace[i] = 0;
}
dn.ofs_in_node++;
i++;
new_size = (loff_t)(dst + i) << PAGE_SHIFT;
if (dst_inode->i_size < new_size)
f2fs_i_size_write(dst_inode, new_size);
} while (--ilen && (do_replace[i] || blkaddr[i] == NULL_ADDR));
f2fs_put_dnode(&dn);
} else {
struct page *psrc, *pdst;
psrc = f2fs_get_lock_data_page(src_inode,
src + i, true);
if (IS_ERR(psrc))
return PTR_ERR(psrc);
pdst = f2fs_get_new_data_page(dst_inode, NULL, dst + i,
true);
if (IS_ERR(pdst)) {
f2fs_put_page(psrc, 1);
return PTR_ERR(pdst);
}
f2fs_copy_page(psrc, pdst);
set_page_dirty(pdst);
f2fs_put_page(pdst, 1);
f2fs_put_page(psrc, 1);
ret = f2fs_truncate_hole(src_inode,
src + i, src + i + 1);
if (ret)
return ret;
i++;
}
}
return 0;
}
static int __exchange_data_block(struct inode *src_inode,
struct inode *dst_inode, pgoff_t src, pgoff_t dst,
pgoff_t len, bool full)
{
block_t *src_blkaddr;
int *do_replace;
pgoff_t olen;
int ret;
while (len) {
olen = min((pgoff_t)4 * ADDRS_PER_BLOCK(src_inode), len);
src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode),
array_size(olen, sizeof(block_t)),
GFP_NOFS);
if (!src_blkaddr)
return -ENOMEM;
do_replace = f2fs_kvzalloc(F2FS_I_SB(src_inode),
array_size(olen, sizeof(int)),
GFP_NOFS);
if (!do_replace) {
kvfree(src_blkaddr);
return -ENOMEM;
}
ret = __read_out_blkaddrs(src_inode, src_blkaddr,
do_replace, src, olen);
if (ret)
goto roll_back;
ret = __clone_blkaddrs(src_inode, dst_inode, src_blkaddr,
do_replace, src, dst, olen, full);
if (ret)
goto roll_back;
src += olen;
dst += olen;
len -= olen;
kvfree(src_blkaddr);
kvfree(do_replace);
}
return 0;
roll_back:
__roll_back_blkaddrs(src_inode, src_blkaddr, do_replace, src, olen);
kvfree(src_blkaddr);
kvfree(do_replace);
return ret;
}
static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t nrpages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
pgoff_t start = offset >> PAGE_SHIFT;
pgoff_t end = (offset + len) >> PAGE_SHIFT;
int ret;
f2fs_balance_fs(sbi, true);
/* avoid gc operation during block exchange */
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_lock_op(sbi);
f2fs_drop_extent_tree(inode);
truncate_pagecache(inode, offset);
ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
f2fs_unlock_op(sbi);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
return ret;
}
static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
{
loff_t new_size;
int ret;
if (offset + len >= i_size_read(inode))
return -EINVAL;
/* collapse range should be aligned to block size of f2fs. */
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
return -EINVAL;
ret = f2fs_convert_inline_inode(inode);
if (ret)
return ret;
/* write out all dirty pages from offset */
ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
if (ret)
return ret;
ret = f2fs_do_collapse(inode, offset, len);
if (ret)
return ret;
/* write out all moved pages, if possible */
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
truncate_pagecache(inode, offset);
new_size = i_size_read(inode) - len;
ret = f2fs_truncate_blocks(inode, new_size, true);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
if (!ret)
f2fs_i_size_write(inode, new_size);
return ret;
}
static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start,
pgoff_t end)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
pgoff_t index = start;
unsigned int ofs_in_node = dn->ofs_in_node;
blkcnt_t count = 0;
int ret;
for (; index < end; index++, dn->ofs_in_node++) {
if (f2fs_data_blkaddr(dn) == NULL_ADDR)
count++;
}
dn->ofs_in_node = ofs_in_node;
ret = f2fs_reserve_new_blocks(dn, count);
if (ret)
return ret;
dn->ofs_in_node = ofs_in_node;
for (index = start; index < end; index++, dn->ofs_in_node++) {
dn->data_blkaddr = f2fs_data_blkaddr(dn);
/*
* f2fs_reserve_new_blocks will not guarantee entire block
* allocation.
*/
if (dn->data_blkaddr == NULL_ADDR) {
ret = -ENOSPC;
break;
}
if (dn->data_blkaddr == NEW_ADDR)
continue;
if (!f2fs_is_valid_blkaddr(sbi, dn->data_blkaddr,
DATA_GENERIC_ENHANCE)) {
ret = -EFSCORRUPTED;
break;
}
f2fs_invalidate_blocks(sbi, dn->data_blkaddr);
dn->data_blkaddr = NEW_ADDR;
f2fs_set_data_blkaddr(dn);
}
f2fs_update_read_extent_cache_range(dn, start, 0, index - start);
f2fs_update_age_extent_cache_range(dn, start, index - start);
return ret;
}
static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
int mode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct address_space *mapping = inode->i_mapping;
pgoff_t index, pg_start, pg_end;
loff_t new_size = i_size_read(inode);
loff_t off_start, off_end;
int ret = 0;
ret = inode_newsize_ok(inode, (len + offset));
if (ret)
return ret;
ret = f2fs_convert_inline_inode(inode);
if (ret)
return ret;
ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1);
if (ret)
return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
off_start = offset & (PAGE_SIZE - 1);
off_end = (offset + len) & (PAGE_SIZE - 1);
if (pg_start == pg_end) {
ret = fill_zero(inode, pg_start, off_start,
off_end - off_start);
if (ret)
return ret;
new_size = max_t(loff_t, new_size, offset + len);
} else {
if (off_start) {
ret = fill_zero(inode, pg_start++, off_start,
PAGE_SIZE - off_start);
if (ret)
return ret;
new_size = max_t(loff_t, new_size,
(loff_t)pg_start << PAGE_SHIFT);
}
for (index = pg_start; index < pg_end;) {
struct dnode_of_data dn;
unsigned int end_offset;
pgoff_t end;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
truncate_pagecache_range(inode,
(loff_t)index << PAGE_SHIFT,
((loff_t)pg_end << PAGE_SHIFT) - 1);
f2fs_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE);
if (ret) {
f2fs_unlock_op(sbi);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
goto out;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
end = min(pg_end, end_offset - dn.ofs_in_node + index);
ret = f2fs_do_zero_range(&dn, index, end);
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_balance_fs(sbi, dn.node_changed);
if (ret)
goto out;
index = end;
new_size = max_t(loff_t, new_size,
(loff_t)index << PAGE_SHIFT);
}
if (off_end) {
ret = fill_zero(inode, pg_end, 0, off_end);
if (ret)
goto out;
new_size = max_t(loff_t, new_size, offset + len);
}
}
out:
if (new_size > i_size_read(inode)) {
if (mode & FALLOC_FL_KEEP_SIZE)
file_set_keep_isize(inode);
else
f2fs_i_size_write(inode, new_size);
}
return ret;
}
static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t nr, pg_start, pg_end, delta, idx;
loff_t new_size;
int ret = 0;
new_size = i_size_read(inode) + len;
ret = inode_newsize_ok(inode, new_size);
if (ret)
return ret;
if (offset >= i_size_read(inode))
return -EINVAL;
/* insert range should be aligned to block size of f2fs. */
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
return -EINVAL;
ret = f2fs_convert_inline_inode(inode);
if (ret)
return ret;
f2fs_balance_fs(sbi, true);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
ret = f2fs_truncate_blocks(inode, i_size_read(inode), true);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
if (ret)
return ret;
/* write out all dirty pages from offset */
ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
if (ret)
return ret;
pg_start = offset >> PAGE_SHIFT;
pg_end = (offset + len) >> PAGE_SHIFT;
delta = pg_end - pg_start;
idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
/* avoid gc operation during block exchange */
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
truncate_pagecache(inode, offset);
while (!ret && idx > pg_start) {
nr = idx - pg_start;
if (nr > delta)
nr = delta;
idx -= nr;
f2fs_lock_op(sbi);
f2fs_drop_extent_tree(inode);
ret = __exchange_data_block(inode, inode, idx,
idx + delta, nr, false);
f2fs_unlock_op(sbi);
}
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
/* write out all moved pages, if possible */
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
truncate_pagecache(inode, offset);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
if (!ret)
f2fs_i_size_write(inode, new_size);
return ret;
}
static int expand_inode_data(struct inode *inode, loff_t offset,
loff_t len, int mode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_map_blocks map = { .m_next_pgofs = NULL,
.m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE,
.m_may_create = true };
pgoff_t pg_start, pg_end;
loff_t new_size = i_size_read(inode);
loff_t off_end;
block_t expanded = 0;
int err;
err = inode_newsize_ok(inode, (len + offset));
if (err)
return err;
err = f2fs_convert_inline_inode(inode);
if (err)
return err;
f2fs_balance_fs(sbi, true);
pg_start = ((unsigned long long)offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long)offset + len) >> PAGE_SHIFT;
off_end = (offset + len) & (PAGE_SIZE - 1);
map.m_lblk = pg_start;
map.m_len = pg_end - pg_start;
if (off_end)
map.m_len++;
if (!map.m_len)
return 0;
if (f2fs_is_pinned_file(inode)) {
block_t sec_blks = BLKS_PER_SEC(sbi);
block_t sec_len = roundup(map.m_len, sec_blks);
map.m_len = sec_blks;
next_alloc:
if (has_not_enough_free_secs(sbi, 0,
GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) {
f2fs_down_write(&sbi->gc_lock);
err = f2fs_gc(sbi, true, false, false, NULL_SEGNO);
if (err && err != -ENODATA && err != -EAGAIN)
goto out_err;
}
f2fs_down_write(&sbi->pin_sem);
f2fs_lock_op(sbi);
f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false);
f2fs_unlock_op(sbi);
map.m_seg_type = CURSEG_COLD_DATA_PINNED;
err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
f2fs_up_write(&sbi->pin_sem);
expanded += map.m_len;
sec_len -= map.m_len;
map.m_lblk += map.m_len;
if (!err && sec_len)
goto next_alloc;
map.m_len = expanded;
} else {
err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO);
expanded = map.m_len;
}
out_err:
if (err) {
pgoff_t last_off;
if (!expanded)
return err;
last_off = pg_start + expanded - 1;
/* update new size to the failed position */
new_size = (last_off == pg_end) ? offset + len :
(loff_t)(last_off + 1) << PAGE_SHIFT;
} else {
new_size = ((loff_t)pg_end << PAGE_SHIFT) + off_end;
}
if (new_size > i_size_read(inode)) {
if (mode & FALLOC_FL_KEEP_SIZE)
file_set_keep_isize(inode);
else
f2fs_i_size_write(inode, new_size);
}
return err;
}
static long f2fs_fallocate(struct file *file, int mode,
loff_t offset, loff_t len)
{
struct inode *inode = file_inode(file);
long ret = 0;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
return -EIO;
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
return -ENOSPC;
if (!f2fs_is_compress_backend_ready(inode))
return -EOPNOTSUPP;
/* f2fs only support ->fallocate for regular file */
if (!S_ISREG(inode->i_mode))
return -EINVAL;
if (IS_ENCRYPTED(inode) &&
(mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE)))
return -EOPNOTSUPP;
if (f2fs_compressed_file(inode) &&
(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE)))
return -EOPNOTSUPP;
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
FALLOC_FL_INSERT_RANGE))
return -EOPNOTSUPP;
inode_lock(inode);
ret = file_modified(file);
if (ret)
goto out;
if (mode & FALLOC_FL_PUNCH_HOLE) {
if (offset >= inode->i_size)
goto out;
ret = punch_hole(inode, offset, len);
} else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
ret = f2fs_collapse_range(inode, offset, len);
} else if (mode & FALLOC_FL_ZERO_RANGE) {
ret = f2fs_zero_range(inode, offset, len, mode);
} else if (mode & FALLOC_FL_INSERT_RANGE) {
ret = f2fs_insert_range(inode, offset, len);
} else {
ret = expand_inode_data(inode, offset, len, mode);
}
if (!ret) {
inode->i_mtime = inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, false);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
}
out:
inode_unlock(inode);
trace_f2fs_fallocate(inode, mode, offset, len, ret);
return ret;
}
static int f2fs_release_file(struct inode *inode, struct file *filp)
{
/*
* f2fs_relase_file is called at every close calls. So we should
* not drop any inmemory pages by close called by other process.
*/
if (!(filp->f_mode & FMODE_WRITE) ||
atomic_read(&inode->i_writecount) != 1)
return 0;
/* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode))
f2fs_drop_inmem_pages(inode);
if (f2fs_is_volatile_file(inode)) {
set_inode_flag(inode, FI_DROP_CACHE);
filemap_fdatawrite(inode->i_mapping);
clear_inode_flag(inode, FI_DROP_CACHE);
clear_inode_flag(inode, FI_VOLATILE_FILE);
stat_dec_volatile_write(inode);
}
return 0;
}
static int f2fs_file_flush(struct file *file, fl_owner_t id)
{
struct inode *inode = file_inode(file);
/*
* If the process doing a transaction is crashed, we should do
* roll-back. Otherwise, other reader/write can see corrupted database
* until all the writers close its file. Since this should be done
* before dropping file lock, it needs to do in ->flush.
*/
if (f2fs_is_atomic_file(inode) &&
F2FS_I(inode)->inmem_task == current)
f2fs_drop_inmem_pages(inode);
return 0;
}
static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
u32 masked_flags = fi->i_flags & mask;
/* mask can be shrunk by flags_valid selector */
iflags &= mask;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
return -EPERM;
if ((iflags ^ masked_flags) & F2FS_CASEFOLD_FL) {
if (!f2fs_sb_has_casefold(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
if (!f2fs_empty_dir(inode))
return -ENOTEMPTY;
}
if (iflags & (F2FS_COMPR_FL | F2FS_NOCOMP_FL)) {
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
if ((iflags & F2FS_COMPR_FL) && (iflags & F2FS_NOCOMP_FL))
return -EINVAL;
}
if ((iflags ^ masked_flags) & F2FS_COMPR_FL) {
if (masked_flags & F2FS_COMPR_FL) {
if (!f2fs_disable_compressed_file(inode))
return -EINVAL;
} else {
if (!f2fs_may_compress(inode))
return -EINVAL;
if (S_ISREG(inode->i_mode) && inode->i_size)
return -EINVAL;
if (set_compress_context(inode))
return -EOPNOTSUPP;
}
}
fi->i_flags = iflags | (fi->i_flags & ~mask);
f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) &&
(fi->i_flags & F2FS_NOCOMP_FL));
if (fi->i_flags & F2FS_PROJINHERIT_FL)
set_inode_flag(inode, FI_PROJ_INHERIT);
else
clear_inode_flag(inode, FI_PROJ_INHERIT);
inode->i_ctime = current_time(inode);
f2fs_set_inode_flags(inode);
f2fs_mark_inode_dirty_sync(inode, true);
return 0;
}
/* FS_IOC_GETFLAGS and FS_IOC_SETFLAGS support */
/*
* To make a new on-disk f2fs i_flag gettable via FS_IOC_GETFLAGS, add an entry
* for it to f2fs_fsflags_map[], and add its FS_*_FL equivalent to
* F2FS_GETTABLE_FS_FL. To also make it settable via FS_IOC_SETFLAGS, also add
* its FS_*_FL equivalent to F2FS_SETTABLE_FS_FL.
*/
static const struct {
u32 iflag;
u32 fsflag;
} f2fs_fsflags_map[] = {
{ F2FS_COMPR_FL, FS_COMPR_FL },
{ F2FS_SYNC_FL, FS_SYNC_FL },
{ F2FS_IMMUTABLE_FL, FS_IMMUTABLE_FL },
{ F2FS_APPEND_FL, FS_APPEND_FL },
{ F2FS_NODUMP_FL, FS_NODUMP_FL },
{ F2FS_NOATIME_FL, FS_NOATIME_FL },
{ F2FS_NOCOMP_FL, FS_NOCOMP_FL },
{ F2FS_INDEX_FL, FS_INDEX_FL },
{ F2FS_DIRSYNC_FL, FS_DIRSYNC_FL },
{ F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL },
{ F2FS_CASEFOLD_FL, FS_CASEFOLD_FL },
};
#define F2FS_GETTABLE_FS_FL ( \
FS_COMPR_FL | \
FS_SYNC_FL | \
FS_IMMUTABLE_FL | \
FS_APPEND_FL | \
FS_NODUMP_FL | \
FS_NOATIME_FL | \
FS_NOCOMP_FL | \
FS_INDEX_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL | \
FS_ENCRYPT_FL | \
FS_INLINE_DATA_FL | \
FS_NOCOW_FL | \
FS_VERITY_FL | \
FS_CASEFOLD_FL)
#define F2FS_SETTABLE_FS_FL ( \
FS_COMPR_FL | \
FS_SYNC_FL | \
FS_IMMUTABLE_FL | \
FS_APPEND_FL | \
FS_NODUMP_FL | \
FS_NOATIME_FL | \
FS_NOCOMP_FL | \
FS_DIRSYNC_FL | \
FS_PROJINHERIT_FL | \
FS_CASEFOLD_FL)
/* Convert f2fs on-disk i_flags to FS_IOC_{GET,SET}FLAGS flags */
static inline u32 f2fs_iflags_to_fsflags(u32 iflags)
{
u32 fsflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_fsflags_map); i++)
if (iflags & f2fs_fsflags_map[i].iflag)
fsflags |= f2fs_fsflags_map[i].fsflag;
return fsflags;
}
/* Convert FS_IOC_{GET,SET}FLAGS flags to f2fs on-disk i_flags */
static inline u32 f2fs_fsflags_to_iflags(u32 fsflags)
{
u32 iflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_fsflags_map); i++)
if (fsflags & f2fs_fsflags_map[i].fsflag)
iflags |= f2fs_fsflags_map[i].iflag;
return iflags;
}
static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
u32 fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
if (IS_ENCRYPTED(inode))
fsflags |= FS_ENCRYPT_FL;
if (IS_VERITY(inode))
fsflags |= FS_VERITY_FL;
if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode))
fsflags |= FS_INLINE_DATA_FL;
if (is_inode_flag_set(inode, FI_PIN_FILE))
fsflags |= FS_NOCOW_FL;
fsflags &= F2FS_GETTABLE_FS_FL;
return put_user(fsflags, (int __user *)arg);
}
static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
u32 fsflags, old_fsflags;
u32 iflags;
int ret;
if (!inode_owner_or_capable(inode))
return -EACCES;
if (get_user(fsflags, (int __user *)arg))
return -EFAULT;
if (fsflags & ~F2FS_GETTABLE_FS_FL)
return -EOPNOTSUPP;
fsflags &= F2FS_SETTABLE_FS_FL;
iflags = f2fs_fsflags_to_iflags(fsflags);
if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
return -EOPNOTSUPP;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
old_fsflags = f2fs_iflags_to_fsflags(fi->i_flags);
ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags);
if (ret)
goto out;
ret = f2fs_setflags_common(inode, iflags,
f2fs_fsflags_to_iflags(F2FS_SETTABLE_FS_FL));
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_getversion(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
return put_user(inode->i_generation, (int __user *)arg);
}
static int f2fs_ioc_start_atomic_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(inode))
return -EACCES;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
if (filp->f_flags & O_DIRECT)
return -EINVAL;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
if (!f2fs_disable_compressed_file(inode)) {
ret = -EINVAL;
goto out;
}
if (f2fs_is_atomic_file(inode)) {
if (is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST))
ret = -EINVAL;
goto out;
}
ret = f2fs_convert_inline_inode(inode);
if (ret)
goto out;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
/*
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
* f2fs_is_atomic_file.
*/
if (get_dirty_pages(inode))
f2fs_warn(F2FS_I_SB(inode), "Unexpected flush for atomic writes: ino=%lu, npages=%u",
inode->i_ino, get_dirty_pages(inode));
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) {
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
goto out;
}
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
if (list_empty(&fi->inmem_ilist))
list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
sbi->atomic_files++;
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
/* add inode in inmem_list first and set atomic_file */
set_inode_flag(inode, FI_ATOMIC_FILE);
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
F2FS_I(inode)->inmem_task = current;
stat_update_max_atomic_write(inode);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_commit_atomic_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(inode))
return -EACCES;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
f2fs_balance_fs(F2FS_I_SB(inode), true);
inode_lock(inode);
if (f2fs_is_volatile_file(inode)) {
ret = -EINVAL;
goto err_out;
}
if (f2fs_is_atomic_file(inode)) {
ret = f2fs_commit_inmem_pages(inode);
if (ret)
goto err_out;
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true);
if (!ret)
f2fs_drop_inmem_pages(inode);
} else {
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false);
}
err_out:
if (is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST)) {
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
ret = -EINVAL;
}
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_start_volatile_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(inode))
return -EACCES;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
if (f2fs_is_volatile_file(inode))
goto out;
ret = f2fs_convert_inline_inode(inode);
if (ret)
goto out;
stat_inc_volatile_write(inode);
stat_update_max_volatile_write(inode);
set_inode_flag(inode, FI_VOLATILE_FILE);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_release_volatile_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(inode))
return -EACCES;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
if (!f2fs_is_volatile_file(inode))
goto out;
if (!f2fs_is_first_block_written(inode)) {
ret = truncate_partial_data_page(inode, 0, true);
goto out;
}
ret = punch_hole(inode, 0, F2FS_BLKSIZE);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_abort_volatile_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
int ret;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!inode_owner_or_capable(inode))
return -EACCES;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
if (f2fs_is_atomic_file(inode))
f2fs_drop_inmem_pages(inode);
if (f2fs_is_volatile_file(inode)) {
clear_inode_flag(inode, FI_VOLATILE_FILE);
stat_dec_volatile_write(inode);
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true);
}
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
inode_unlock(inode);
mnt_drop_write_file(filp);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return ret;
}
static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct super_block *sb = sbi->sb;
__u32 in;
int ret = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(in, (__u32 __user *)arg))
return -EFAULT;
if (in != F2FS_GOING_DOWN_FULLSYNC) {
ret = mnt_want_write_file(filp);
if (ret) {
if (ret == -EROFS) {
ret = 0;
f2fs_stop_checkpoint(sbi, false,
STOP_CP_REASON_SHUTDOWN);
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
trace_f2fs_shutdown(sbi, in, ret);
}
return ret;
}
}
switch (in) {
case F2FS_GOING_DOWN_FULLSYNC:
ret = freeze_bdev(sb->s_bdev);
if (ret)
goto out;
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
thaw_bdev(sb->s_bdev);
break;
case F2FS_GOING_DOWN_METASYNC:
/* do checkpoint only */
ret = f2fs_sync_fs(sb, 1);
if (ret)
goto out;
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
break;
case F2FS_GOING_DOWN_NOSYNC:
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
break;
case F2FS_GOING_DOWN_METAFLUSH:
f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_META_IO);
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
break;
case F2FS_GOING_DOWN_NEED_FSCK:
set_sbi_flag(sbi, SBI_NEED_FSCK);
set_sbi_flag(sbi, SBI_CP_DISABLED_QUICK);
set_sbi_flag(sbi, SBI_IS_DIRTY);
/* do checkpoint only */
ret = f2fs_sync_fs(sb, 1);
goto out;
default:
ret = -EINVAL;
goto out;
}
f2fs_stop_gc_thread(sbi);
f2fs_stop_discard_thread(sbi);
f2fs_drop_discard_cmd(sbi);
clear_opt(sbi, DISCARD);
f2fs_update_time(sbi, REQ_TIME);
out:
if (in != F2FS_GOING_DOWN_FULLSYNC)
mnt_drop_write_file(filp);
trace_f2fs_shutdown(sbi, in, ret);
return ret;
}
static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct super_block *sb = inode->i_sb;
struct request_queue *q = bdev_get_queue(sb->s_bdev);
struct fstrim_range range;
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!f2fs_hw_support_discard(F2FS_SB(sb)))
return -EOPNOTSUPP;
if (copy_from_user(&range, (struct fstrim_range __user *)arg,
sizeof(range)))
return -EFAULT;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
range.minlen = max((unsigned int)range.minlen,
q->limits.discard_granularity);
ret = f2fs_trim_fs(F2FS_SB(sb), &range);
mnt_drop_write_file(filp);
if (ret < 0)
return ret;
if (copy_to_user((struct fstrim_range __user *)arg, &range,
sizeof(range)))
return -EFAULT;
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return 0;
}
static bool uuid_is_nonzero(__u8 u[16])
{
int i;
for (i = 0; i < 16; i++)
if (u[i])
return true;
return false;
}
static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
if (!f2fs_sb_has_encrypt(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return fscrypt_ioctl_set_policy(filp, (const void __user *)arg);
}
static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
}
static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int err;
if (!f2fs_sb_has_encrypt(sbi))
return -EOPNOTSUPP;
err = mnt_want_write_file(filp);
if (err)
return err;
f2fs_down_write(&sbi->sb_lock);
if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt))
goto got_it;
/* update superblock with uuid */
generate_random_uuid(sbi->raw_super->encrypt_pw_salt);
err = f2fs_commit_super(sbi, false);
if (err) {
/* undo new data */
memset(sbi->raw_super->encrypt_pw_salt, 0, 16);
goto out_err;
}
got_it:
if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt,
16))
err = -EFAULT;
out_err:
f2fs_up_write(&sbi->sb_lock);
mnt_drop_write_file(filp);
return err;
}
static int f2fs_ioc_get_encryption_policy_ex(struct file *filp,
unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_get_policy_ex(filp, (void __user *)arg);
}
static int f2fs_ioc_add_encryption_key(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_add_key(filp, (void __user *)arg);
}
static int f2fs_ioc_remove_encryption_key(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_remove_key(filp, (void __user *)arg);
}
static int f2fs_ioc_remove_encryption_key_all_users(struct file *filp,
unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_remove_key_all_users(filp, (void __user *)arg);
}
static int f2fs_ioc_get_encryption_key_status(struct file *filp,
unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_get_key_status(filp, (void __user *)arg);
}
static int f2fs_ioc_get_encryption_nonce(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_encrypt(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fscrypt_ioctl_get_nonce(filp, (void __user *)arg);
}
static int f2fs_ioc_gc(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
__u32 sync;
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(sync, (__u32 __user *)arg))
return -EFAULT;
if (f2fs_readonly(sbi->sb))
return -EROFS;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
if (!sync) {
if (!f2fs_down_write_trylock(&sbi->gc_lock)) {
ret = -EBUSY;
goto out;
}
} else {
f2fs_down_write(&sbi->gc_lock);
}
ret = f2fs_gc(sbi, sync, true, false, NULL_SEGNO);
out:
mnt_drop_write_file(filp);
return ret;
}
static int __f2fs_ioc_gc_range(struct file *filp, struct f2fs_gc_range *range)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
u64 end;
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (f2fs_readonly(sbi->sb))
return -EROFS;
end = range->start + range->len;
if (end < range->start || range->start < MAIN_BLKADDR(sbi) ||
end >= MAX_BLKADDR(sbi))
return -EINVAL;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
do_more:
if (!range->sync) {
if (!f2fs_down_write_trylock(&sbi->gc_lock)) {
ret = -EBUSY;
goto out;
}
} else {
f2fs_down_write(&sbi->gc_lock);
}
ret = f2fs_gc(sbi, range->sync, true, false,
GET_SEGNO(sbi, range->start));
if (ret) {
if (ret == -EBUSY)
ret = -EAGAIN;
goto out;
}
range->start += BLKS_PER_SEC(sbi);
if (range->start <= end)
goto do_more;
out:
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg)
{
struct f2fs_gc_range range;
if (copy_from_user(&range, (struct f2fs_gc_range __user *)arg,
sizeof(range)))
return -EFAULT;
return __f2fs_ioc_gc_range(filp, &range);
}
static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (f2fs_readonly(sbi->sb))
return -EROFS;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
f2fs_info(sbi, "Skipping Checkpoint. Checkpoints currently disabled.");
return -EINVAL;
}
ret = mnt_want_write_file(filp);
if (ret)
return ret;
ret = f2fs_sync_fs(sbi->sb, 1);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
struct file *filp,
struct f2fs_defragment *range)
{
struct inode *inode = file_inode(filp);
struct f2fs_map_blocks map = { .m_next_extent = NULL,
.m_seg_type = NO_CHECK_TYPE,
.m_may_create = false };
struct extent_info ei = {};
pgoff_t pg_start, pg_end, next_pgofs;
unsigned int blk_per_seg = sbi->blocks_per_seg;
unsigned int total = 0, sec_num;
block_t blk_end = 0;
bool fragmented = false;
int err;
pg_start = range->start >> PAGE_SHIFT;
pg_end = (range->start + range->len) >> PAGE_SHIFT;
f2fs_balance_fs(sbi, true);
inode_lock(inode);
/* 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)) {
err = -EINVAL;
goto out;
}
/* writeback all dirty pages in the range */
err = filemap_write_and_wait_range(inode->i_mapping, range->start,
range->start + range->len - 1);
if (err)
goto out;
/*
* lookup mapping info in extent cache, skip defragmenting if physical
* block addresses are continuous.
*/
if (f2fs_lookup_read_extent_cache(inode, pg_start, &ei)) {
if (ei.fofs + ei.len >= pg_end)
goto out;
}
map.m_lblk = pg_start;
map.m_next_pgofs = &next_pgofs;
/*
* lookup mapping info in dnode page cache, skip defragmenting if all
* physical block addresses are continuous even if there are hole(s)
* in logical blocks.
*/
while (map.m_lblk < pg_end) {
map.m_len = pg_end - map.m_lblk;
err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT);
if (err)
goto out;
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
map.m_lblk = next_pgofs;
continue;
}
if (blk_end && blk_end != map.m_pblk)
fragmented = true;
/* record total count of block that we're going to move */
total += map.m_len;
blk_end = map.m_pblk + map.m_len;
map.m_lblk += map.m_len;
}
if (!fragmented) {
total = 0;
goto out;
}
sec_num = DIV_ROUND_UP(total, BLKS_PER_SEC(sbi));
/*
* make sure there are enough free section for LFS allocation, this can
* avoid defragment running in SSR mode when free section are allocated
* intensively
*/
if (has_not_enough_free_secs(sbi, 0, sec_num)) {
err = -EAGAIN;
goto out;
}
map.m_lblk = pg_start;
map.m_len = pg_end - pg_start;
total = 0;
while (map.m_lblk < pg_end) {
pgoff_t idx;
int cnt = 0;
do_map:
map.m_len = pg_end - map.m_lblk;
err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT);
if (err)
goto clear_out;
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
map.m_lblk = next_pgofs;
goto check;
}
set_inode_flag(inode, FI_SKIP_WRITES);
idx = map.m_lblk;
while (idx < map.m_lblk + map.m_len && cnt < blk_per_seg) {
struct page *page;
page = f2fs_get_lock_data_page(inode, idx, true);
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto clear_out;
}
set_page_dirty(page);
f2fs_put_page(page, 1);
idx++;
cnt++;
total++;
}
map.m_lblk = idx;
check:
if (map.m_lblk < pg_end && cnt < blk_per_seg)
goto do_map;
clear_inode_flag(inode, FI_SKIP_WRITES);
err = filemap_fdatawrite(inode->i_mapping);
if (err)
goto out;
}
clear_out:
clear_inode_flag(inode, FI_SKIP_WRITES);
out:
clear_inode_flag(inode, FI_OPU_WRITE);
inode_unlock(inode);
if (!err)
range->len = (u64)total << PAGE_SHIFT;
return err;
}
static int f2fs_ioc_defragment(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_defragment range;
int err;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (!S_ISREG(inode->i_mode) || f2fs_is_atomic_file(inode))
return -EINVAL;
if (f2fs_readonly(sbi->sb))
return -EROFS;
if (copy_from_user(&range, (struct f2fs_defragment __user *)arg,
sizeof(range)))
return -EFAULT;
/* verify alignment of offset & size */
if (range.start & (F2FS_BLKSIZE - 1) || range.len & (F2FS_BLKSIZE - 1))
return -EINVAL;
if (unlikely((range.start + range.len) >> PAGE_SHIFT >
max_file_blocks(inode)))
return -EINVAL;
err = mnt_want_write_file(filp);
if (err)
return err;
err = f2fs_defragment_range(sbi, filp, &range);
mnt_drop_write_file(filp);
f2fs_update_time(sbi, REQ_TIME);
if (err < 0)
return err;
if (copy_to_user((struct f2fs_defragment __user *)arg, &range,
sizeof(range)))
return -EFAULT;
return 0;
}
static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, size_t len)
{
struct inode *src = file_inode(file_in);
struct inode *dst = file_inode(file_out);
struct f2fs_sb_info *sbi = F2FS_I_SB(src);
size_t olen = len, dst_max_i_size = 0;
size_t dst_osize;
int ret;
if (file_in->f_path.mnt != file_out->f_path.mnt ||
src->i_sb != dst->i_sb)
return -EXDEV;
if (unlikely(f2fs_readonly(src->i_sb)))
return -EROFS;
if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode))
return -EINVAL;
if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst))
return -EOPNOTSUPP;
if (pos_out < 0 || pos_in < 0)
return -EINVAL;
if (src == dst) {
if (pos_in == pos_out)
return 0;
if (pos_out > pos_in && pos_out < pos_in + len)
return -EINVAL;
}
inode_lock(src);
if (src != dst) {
ret = -EBUSY;
if (!inode_trylock(dst))
goto out;
}
if (f2fs_compressed_file(src) || f2fs_compressed_file(dst)) {
ret = -EOPNOTSUPP;
goto out_unlock;
}
ret = -EINVAL;
if (pos_in + len > src->i_size || pos_in + len < pos_in)
goto out_unlock;
if (len == 0)
olen = len = src->i_size - pos_in;
if (pos_in + len == src->i_size)
len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in;
if (len == 0) {
ret = 0;
goto out_unlock;
}
dst_osize = dst->i_size;
if (pos_out + olen > dst->i_size)
dst_max_i_size = pos_out + olen;
/* verify the end result is block aligned */
if (!IS_ALIGNED(pos_in, F2FS_BLKSIZE) ||
!IS_ALIGNED(pos_in + len, F2FS_BLKSIZE) ||
!IS_ALIGNED(pos_out, F2FS_BLKSIZE))
goto out_unlock;
ret = f2fs_convert_inline_inode(src);
if (ret)
goto out_unlock;
ret = f2fs_convert_inline_inode(dst);
if (ret)
goto out_unlock;
/* write out all dirty pages from offset */
ret = filemap_write_and_wait_range(src->i_mapping,
pos_in, pos_in + len);
if (ret)
goto out_unlock;
ret = filemap_write_and_wait_range(dst->i_mapping,
pos_out, pos_out + len);
if (ret)
goto out_unlock;
f2fs_balance_fs(sbi, true);
f2fs_down_write(&F2FS_I(src)->i_gc_rwsem[WRITE]);
if (src != dst) {
ret = -EBUSY;
if (!f2fs_down_write_trylock(&F2FS_I(dst)->i_gc_rwsem[WRITE]))
goto out_src;
}
f2fs_lock_op(sbi);
ret = __exchange_data_block(src, dst, pos_in >> F2FS_BLKSIZE_BITS,
pos_out >> F2FS_BLKSIZE_BITS,
len >> F2FS_BLKSIZE_BITS, false);
if (!ret) {
if (dst_max_i_size)
f2fs_i_size_write(dst, dst_max_i_size);
else if (dst_osize != dst->i_size)
f2fs_i_size_write(dst, dst_osize);
}
f2fs_unlock_op(sbi);
if (src != dst)
f2fs_up_write(&F2FS_I(dst)->i_gc_rwsem[WRITE]);
out_src:
f2fs_up_write(&F2FS_I(src)->i_gc_rwsem[WRITE]);
out_unlock:
if (src != dst)
inode_unlock(dst);
out:
inode_unlock(src);
return ret;
}
static int __f2fs_ioc_move_range(struct file *filp,
struct f2fs_move_range *range)
{
struct fd dst;
int err;
if (!(filp->f_mode & FMODE_READ) ||
!(filp->f_mode & FMODE_WRITE))
return -EBADF;
dst = fdget(range->dst_fd);
if (!dst.file)
return -EBADF;
if (!(dst.file->f_mode & FMODE_WRITE)) {
err = -EBADF;
goto err_out;
}
err = mnt_want_write_file(filp);
if (err)
goto err_out;
err = f2fs_move_file_range(filp, range->pos_in, dst.file,
range->pos_out, range->len);
mnt_drop_write_file(filp);
err_out:
fdput(dst);
return err;
}
static int f2fs_ioc_move_range(struct file *filp, unsigned long arg)
{
struct f2fs_move_range range;
if (copy_from_user(&range, (struct f2fs_move_range __user *)arg,
sizeof(range)))
return -EFAULT;
return __f2fs_ioc_move_range(filp, &range);
}
static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct sit_info *sm = SIT_I(sbi);
unsigned int start_segno = 0, end_segno = 0;
unsigned int dev_start_segno = 0, dev_end_segno = 0;
struct f2fs_flush_device range;
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (f2fs_readonly(sbi->sb))
return -EROFS;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
return -EINVAL;
if (copy_from_user(&range, (struct f2fs_flush_device __user *)arg,
sizeof(range)))
return -EFAULT;
if (!f2fs_is_multi_device(sbi) || sbi->s_ndevs - 1 <= range.dev_num ||
__is_large_section(sbi)) {
f2fs_warn(sbi, "Can't flush %u in %d for segs_per_sec %u != 1",
range.dev_num, sbi->s_ndevs, sbi->segs_per_sec);
return -EINVAL;
}
ret = mnt_want_write_file(filp);
if (ret)
return ret;
if (range.dev_num != 0)
dev_start_segno = GET_SEGNO(sbi, FDEV(range.dev_num).start_blk);
dev_end_segno = GET_SEGNO(sbi, FDEV(range.dev_num).end_blk);
start_segno = sm->last_victim[FLUSH_DEVICE];
if (start_segno < dev_start_segno || start_segno >= dev_end_segno)
start_segno = dev_start_segno;
end_segno = min(start_segno + range.segments, dev_end_segno);
while (start_segno < end_segno) {
if (!f2fs_down_write_trylock(&sbi->gc_lock)) {
ret = -EBUSY;
goto out;
}
sm->last_victim[GC_CB] = end_segno + 1;
sm->last_victim[GC_GREEDY] = end_segno + 1;
sm->last_victim[ALLOC_NEXT] = end_segno + 1;
ret = f2fs_gc(sbi, true, true, true, start_segno);
if (ret == -EAGAIN)
ret = 0;
else if (ret < 0)
break;
start_segno++;
}
out:
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_get_features(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
u32 sb_feature = le32_to_cpu(F2FS_I_SB(inode)->raw_super->feature);
/* Must validate to set it with SQLite behavior in Android. */
sb_feature |= F2FS_FEATURE_ATOMIC_WRITE;
return put_user(sb_feature, (u32 __user *)arg);
}
#ifdef CONFIG_QUOTA
int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid)
{
struct dquot *transfer_to[MAXQUOTAS] = {};
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct super_block *sb = sbi->sb;
int err;
transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
if (IS_ERR(transfer_to[PRJQUOTA]))
return PTR_ERR(transfer_to[PRJQUOTA]);
err = __dquot_transfer(inode, transfer_to);
if (err)
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
dqput(transfer_to[PRJQUOTA]);
return err;
}
static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
{
struct inode *inode = file_inode(filp);
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *ipage;
kprojid_t kprojid;
int err;
if (!f2fs_sb_has_project_quota(sbi)) {
if (projid != F2FS_DEF_PROJID)
return -EOPNOTSUPP;
else
return 0;
}
if (!f2fs_has_extra_attr(inode))
return -EOPNOTSUPP;
kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
if (projid_eq(kprojid, F2FS_I(inode)->i_projid))
return 0;
err = -EPERM;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
return err;
ipage = f2fs_get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage))
return PTR_ERR(ipage);
if (!F2FS_FITS_IN_INODE(F2FS_INODE(ipage), fi->i_extra_isize,
i_projid)) {
err = -EOVERFLOW;
f2fs_put_page(ipage, 1);
return err;
}
f2fs_put_page(ipage, 1);
err = dquot_initialize(inode);
if (err)
return err;
f2fs_lock_op(sbi);
err = f2fs_transfer_project_quota(inode, kprojid);
if (err)
goto out_unlock;
F2FS_I(inode)->i_projid = kprojid;
inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, true);
out_unlock:
f2fs_unlock_op(sbi);
return err;
}
#else
int f2fs_transfer_project_quota(struct inode *inode, kprojid_t kprojid)
{
return 0;
}
static int f2fs_ioc_setproject(struct file *filp, __u32 projid)
{
if (projid != F2FS_DEF_PROJID)
return -EOPNOTSUPP;
return 0;
}
#endif
/* FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR support */
/*
* To make a new on-disk f2fs i_flag gettable via FS_IOC_FSGETXATTR and settable
* via FS_IOC_FSSETXATTR, add an entry for it to f2fs_xflags_map[], and add its
* FS_XFLAG_* equivalent to F2FS_SUPPORTED_XFLAGS.
*/
static const struct {
u32 iflag;
u32 xflag;
} f2fs_xflags_map[] = {
{ F2FS_SYNC_FL, FS_XFLAG_SYNC },
{ F2FS_IMMUTABLE_FL, FS_XFLAG_IMMUTABLE },
{ F2FS_APPEND_FL, FS_XFLAG_APPEND },
{ F2FS_NODUMP_FL, FS_XFLAG_NODUMP },
{ F2FS_NOATIME_FL, FS_XFLAG_NOATIME },
{ F2FS_PROJINHERIT_FL, FS_XFLAG_PROJINHERIT },
};
#define F2FS_SUPPORTED_XFLAGS ( \
FS_XFLAG_SYNC | \
FS_XFLAG_IMMUTABLE | \
FS_XFLAG_APPEND | \
FS_XFLAG_NODUMP | \
FS_XFLAG_NOATIME | \
FS_XFLAG_PROJINHERIT)
/* Convert f2fs on-disk i_flags to FS_IOC_FS{GET,SET}XATTR flags */
static inline u32 f2fs_iflags_to_xflags(u32 iflags)
{
u32 xflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
if (iflags & f2fs_xflags_map[i].iflag)
xflags |= f2fs_xflags_map[i].xflag;
return xflags;
}
/* Convert FS_IOC_FS{GET,SET}XATTR flags to f2fs on-disk i_flags */
static inline u32 f2fs_xflags_to_iflags(u32 xflags)
{
u32 iflags = 0;
int i;
for (i = 0; i < ARRAY_SIZE(f2fs_xflags_map); i++)
if (xflags & f2fs_xflags_map[i].xflag)
iflags |= f2fs_xflags_map[i].iflag;
return iflags;
}
static void f2fs_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
simple_fill_fsxattr(fa, f2fs_iflags_to_xflags(fi->i_flags));
if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
fa->fsx_projid = from_kprojid(&init_user_ns, fi->i_projid);
}
static int f2fs_ioc_fsgetxattr(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct fsxattr fa;
f2fs_fill_fsxattr(inode, &fa);
if (copy_to_user((struct fsxattr __user *)arg, &fa, sizeof(fa)))
return -EFAULT;
return 0;
}
static int f2fs_ioc_fssetxattr(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct fsxattr fa, old_fa;
u32 iflags;
int err;
if (copy_from_user(&fa, (struct fsxattr __user *)arg, sizeof(fa)))
return -EFAULT;
/* Make sure caller has proper permission */
if (!inode_owner_or_capable(inode))
return -EACCES;
if (fa.fsx_xflags & ~F2FS_SUPPORTED_XFLAGS)
return -EOPNOTSUPP;
iflags = f2fs_xflags_to_iflags(fa.fsx_xflags);
if (f2fs_mask_flags(inode->i_mode, iflags) != iflags)
return -EOPNOTSUPP;
err = mnt_want_write_file(filp);
if (err)
return err;
inode_lock(inode);
f2fs_fill_fsxattr(inode, &old_fa);
err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
if (err)
goto out;
err = f2fs_setflags_common(inode, iflags,
f2fs_xflags_to_iflags(F2FS_SUPPORTED_XFLAGS));
if (err)
goto out;
err = f2fs_ioc_setproject(filp, fa.fsx_projid);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return err;
}
int f2fs_pin_file_control(struct inode *inode, bool inc)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
/* Use i_gc_failures for normal file as a risk signal. */
if (inc)
f2fs_i_gc_failures_write(inode,
fi->i_gc_failures[GC_FAILURE_PIN] + 1);
if (fi->i_gc_failures[GC_FAILURE_PIN] > sbi->gc_pin_file_threshold) {
f2fs_warn(sbi, "%s: Enable GC = ino %lx after %x GC trials",
__func__, inode->i_ino,
fi->i_gc_failures[GC_FAILURE_PIN]);
clear_inode_flag(inode, FI_PIN_FILE);
return -EAGAIN;
}
return 0;
}
static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
__u32 pin;
int ret = 0;
if (get_user(pin, (__u32 __user *)arg))
return -EFAULT;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
if (f2fs_readonly(F2FS_I_SB(inode)->sb))
return -EROFS;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
inode_lock(inode);
if (!pin) {
clear_inode_flag(inode, FI_PIN_FILE);
f2fs_i_gc_failures_write(inode, 0);
goto done;
}
if (f2fs_should_update_outplace(inode, NULL)) {
ret = -EINVAL;
goto out;
}
if (f2fs_pin_file_control(inode, false)) {
ret = -EAGAIN;
goto out;
}
ret = f2fs_convert_inline_inode(inode);
if (ret)
goto out;
if (!f2fs_disable_compressed_file(inode)) {
ret = -EOPNOTSUPP;
goto out;
}
set_inode_flag(inode, FI_PIN_FILE);
ret = F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN];
done:
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
__u32 pin = 0;
if (is_inode_flag_set(inode, FI_PIN_FILE))
pin = F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN];
return put_user(pin, (u32 __user *)arg);
}
int f2fs_precache_extents(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_map_blocks map;
pgoff_t m_next_extent;
loff_t end;
int err;
if (is_inode_flag_set(inode, FI_NO_EXTENT))
return -EOPNOTSUPP;
map.m_lblk = 0;
map.m_pblk = 0;
map.m_next_pgofs = NULL;
map.m_next_extent = &m_next_extent;
map.m_seg_type = NO_CHECK_TYPE;
map.m_may_create = false;
end = max_file_blocks(inode);
while (map.m_lblk < end) {
map.m_len = end - map.m_lblk;
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_PRECACHE);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
if (err)
return err;
map.m_lblk = m_next_extent;
}
return 0;
}
static int f2fs_ioc_precache_extents(struct file *filp, unsigned long arg)
{
return f2fs_precache_extents(file_inode(filp));
}
static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
__u64 block_count;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (f2fs_readonly(sbi->sb))
return -EROFS;
if (copy_from_user(&block_count, (void __user *)arg,
sizeof(block_count)))
return -EFAULT;
return f2fs_resize_fs(filp, block_count);
}
static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
if (!f2fs_sb_has_verity(F2FS_I_SB(inode))) {
f2fs_warn(F2FS_I_SB(inode),
"Can't enable fs-verity on inode %lu: the verity feature is not enabled on this filesystem",
inode->i_ino);
return -EOPNOTSUPP;
}
return fsverity_ioctl_enable(filp, (const void __user *)arg);
}
static int f2fs_ioc_measure_verity(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_verity(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fsverity_ioctl_measure(filp, (void __user *)arg);
}
static int f2fs_ioc_read_verity_metadata(struct file *filp, unsigned long arg)
{
if (!f2fs_sb_has_verity(F2FS_I_SB(file_inode(filp))))
return -EOPNOTSUPP;
return fsverity_ioctl_read_metadata(filp, (const void __user *)arg);
}
static int f2fs_ioc_getfslabel(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
char *vbuf;
int count;
int err = 0;
vbuf = f2fs_kzalloc(sbi, MAX_VOLUME_NAME, GFP_KERNEL);
if (!vbuf)
return -ENOMEM;
f2fs_down_read(&sbi->sb_lock);
count = utf16s_to_utf8s(sbi->raw_super->volume_name,
ARRAY_SIZE(sbi->raw_super->volume_name),
UTF16_LITTLE_ENDIAN, vbuf, MAX_VOLUME_NAME);
f2fs_up_read(&sbi->sb_lock);
if (copy_to_user((char __user *)arg, vbuf,
min(FSLABEL_MAX, count)))
err = -EFAULT;
kfree(vbuf);
return err;
}
static int f2fs_ioc_setfslabel(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
char *vbuf;
int err = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
vbuf = strndup_user((const char __user *)arg, FSLABEL_MAX);
if (IS_ERR(vbuf))
return PTR_ERR(vbuf);
err = mnt_want_write_file(filp);
if (err)
goto out;
f2fs_down_write(&sbi->sb_lock);
memset(sbi->raw_super->volume_name, 0,
sizeof(sbi->raw_super->volume_name));
utf8s_to_utf16s(vbuf, strlen(vbuf), UTF16_LITTLE_ENDIAN,
sbi->raw_super->volume_name,
ARRAY_SIZE(sbi->raw_super->volume_name));
err = f2fs_commit_super(sbi, false);
f2fs_up_write(&sbi->sb_lock);
mnt_drop_write_file(filp);
out:
kfree(vbuf);
return err;
}
static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
__u64 blocks;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
if (!f2fs_compressed_file(inode))
return -EINVAL;
blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
return put_user(blocks, (u64 __user *)arg);
}
static int release_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
unsigned int released_blocks = 0;
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
block_t blkaddr;
int i;
for (i = 0; i < count; i++) {
blkaddr = data_blkaddr(dn->inode, dn->node_page,
dn->ofs_in_node + i);
if (!__is_valid_data_blkaddr(blkaddr))
continue;
if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
DATA_GENERIC_ENHANCE)))
return -EFSCORRUPTED;
}
while (count) {
int compr_blocks = 0;
for (i = 0; i < cluster_size; i++, dn->ofs_in_node++) {
blkaddr = f2fs_data_blkaddr(dn);
if (i == 0) {
if (blkaddr == COMPRESS_ADDR)
continue;
dn->ofs_in_node += cluster_size;
goto next;
}
if (__is_valid_data_blkaddr(blkaddr))
compr_blocks++;
if (blkaddr != NEW_ADDR)
continue;
dn->data_blkaddr = NULL_ADDR;
f2fs_set_data_blkaddr(dn);
}
f2fs_i_compr_blocks_update(dn->inode, compr_blocks, false);
dec_valid_block_count(sbi, dn->inode,
cluster_size - compr_blocks);
released_blocks += cluster_size - compr_blocks;
next:
count -= cluster_size;
}
return released_blocks;
}
static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int released_blocks = 0;
int ret;
int writecount;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
if (!f2fs_compressed_file(inode))
return -EINVAL;
if (f2fs_readonly(sbi->sb))
return -EROFS;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
f2fs_balance_fs(F2FS_I_SB(inode), true);
inode_lock(inode);
writecount = atomic_read(&inode->i_writecount);
if ((filp->f_mode & FMODE_WRITE && writecount != 1) ||
(!(filp->f_mode & FMODE_WRITE) && writecount)) {
ret = -EBUSY;
goto out;
}
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
ret = -EINVAL;
goto out;
}
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
goto out;
set_inode_flag(inode, FI_COMPRESS_RELEASED);
inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, true);
if (!atomic_read(&F2FS_I(inode)->i_compr_blocks))
goto out;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
while (page_idx < last_idx) {
struct dnode_of_data dn;
pgoff_t end_offset, count;
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE);
if (ret) {
if (ret == -ENOENT) {
page_idx = f2fs_get_next_page_offset(&dn,
page_idx);
ret = 0;
continue;
}
break;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
count = min(end_offset - dn.ofs_in_node, last_idx - page_idx);
count = round_up(count, F2FS_I(inode)->i_cluster_size);
ret = release_compress_blocks(&dn, count);
f2fs_put_dnode(&dn);
if (ret < 0)
break;
page_idx += count;
released_blocks += ret;
}
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
out:
inode_unlock(inode);
mnt_drop_write_file(filp);
if (ret >= 0) {
ret = put_user(released_blocks, (u64 __user *)arg);
} else if (released_blocks &&
atomic_read(&F2FS_I(inode)->i_compr_blocks)) {
set_sbi_flag(sbi, SBI_NEED_FSCK);
f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
"iblocks=%llu, released=%u, compr_blocks=%u, "
"run fsck to fix.",
__func__, inode->i_ino, inode->i_blocks,
released_blocks,
atomic_read(&F2FS_I(inode)->i_compr_blocks));
}
return ret;
}
static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
unsigned int reserved_blocks = 0;
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
block_t blkaddr;
int i;
for (i = 0; i < count; i++) {
blkaddr = data_blkaddr(dn->inode, dn->node_page,
dn->ofs_in_node + i);
if (!__is_valid_data_blkaddr(blkaddr))
continue;
if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
DATA_GENERIC_ENHANCE)))
return -EFSCORRUPTED;
}
while (count) {
int compr_blocks = 0;
blkcnt_t reserved = 0;
blkcnt_t to_reserved;
int ret;
for (i = 0; i < cluster_size; i++, dn->ofs_in_node++) {
blkaddr = f2fs_data_blkaddr(dn);
if (i == 0) {
if (blkaddr == COMPRESS_ADDR)
continue;
dn->ofs_in_node += cluster_size;
goto next;
}
/*
* compressed cluster was not released due to it
* fails in release_compress_blocks(), so NEW_ADDR
* is a possible case.
*/
if (blkaddr == NEW_ADDR) {
reserved++;
continue;
}
if (__is_valid_data_blkaddr(blkaddr)) {
compr_blocks++;
continue;
}
dn->data_blkaddr = NEW_ADDR;
f2fs_set_data_blkaddr(dn);
}
to_reserved = cluster_size - compr_blocks - reserved;
/* for the case all blocks in cluster were reserved */
if (to_reserved == 1) {
dn->ofs_in_node += cluster_size;
goto next;
}
ret = inc_valid_block_count(sbi, dn->inode, &to_reserved);
if (ret)
return ret;
if (reserved != cluster_size - compr_blocks)
return -ENOSPC;
f2fs_i_compr_blocks_update(dn->inode, compr_blocks, true);
reserved_blocks += to_reserved;
next:
count -= cluster_size;
}
return reserved_blocks;
}
static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int reserved_blocks = 0;
int ret;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
if (!f2fs_compressed_file(inode))
return -EINVAL;
if (f2fs_readonly(sbi->sb))
return -EROFS;
ret = mnt_want_write_file(filp);
if (ret)
return ret;
if (atomic_read(&F2FS_I(inode)->i_compr_blocks))
goto out;
f2fs_balance_fs(F2FS_I_SB(inode), true);
inode_lock(inode);
if (!is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
ret = -EINVAL;
goto unlock_inode;
}
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
while (page_idx < last_idx) {
struct dnode_of_data dn;
pgoff_t end_offset, count;
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE);
if (ret) {
if (ret == -ENOENT) {
page_idx = f2fs_get_next_page_offset(&dn,
page_idx);
ret = 0;
continue;
}
break;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
count = min(end_offset - dn.ofs_in_node, last_idx - page_idx);
count = round_up(count, F2FS_I(inode)->i_cluster_size);
ret = reserve_compress_blocks(&dn, count);
f2fs_put_dnode(&dn);
if (ret < 0)
break;
page_idx += count;
reserved_blocks += ret;
}
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
if (ret >= 0) {
clear_inode_flag(inode, FI_COMPRESS_RELEASED);
inode->i_ctime = current_time(inode);
f2fs_mark_inode_dirty_sync(inode, true);
}
unlock_inode:
inode_unlock(inode);
out:
mnt_drop_write_file(filp);
if (ret >= 0) {
ret = put_user(reserved_blocks, (u64 __user *)arg);
} else if (reserved_blocks &&
atomic_read(&F2FS_I(inode)->i_compr_blocks)) {
set_sbi_flag(sbi, SBI_NEED_FSCK);
f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
"iblocks=%llu, reserved=%u, compr_blocks=%u, "
"run fsck to fix.",
__func__, inode->i_ino, inode->i_blocks,
reserved_blocks,
atomic_read(&F2FS_I(inode)->i_compr_blocks));
}
return ret;
}
static int f2fs_secure_erase(struct block_device *bdev, struct inode *inode,
pgoff_t off, block_t block, block_t len, u32 flags)
{
struct request_queue *q = bdev_get_queue(bdev);
sector_t sector = SECTOR_FROM_BLOCK(block);
sector_t nr_sects = SECTOR_FROM_BLOCK(len);
int ret = 0;
if (!q)
return -ENXIO;
if (flags & F2FS_TRIM_FILE_DISCARD)
ret = blkdev_issue_discard(bdev, sector, nr_sects, GFP_NOFS,
blk_queue_secure_erase(q) ?
BLKDEV_DISCARD_SECURE : 0);
if (!ret && (flags & F2FS_TRIM_FILE_ZEROOUT)) {
if (IS_ENCRYPTED(inode))
ret = fscrypt_zeroout_range(inode, off, block, len);
else
ret = blkdev_issue_zeroout(bdev, sector, nr_sects,
GFP_NOFS, 0);
}
return ret;
}
static int f2fs_sec_trim_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct address_space *mapping = inode->i_mapping;
struct block_device *prev_bdev = NULL;
struct f2fs_sectrim_range range;
pgoff_t index, pg_end, prev_index = 0;
block_t prev_block = 0, len = 0;
loff_t end_addr;
bool to_end = false;
int ret = 0;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (copy_from_user(&range, (struct f2fs_sectrim_range __user *)arg,
sizeof(range)))
return -EFAULT;
if (range.flags == 0 || (range.flags & ~F2FS_TRIM_FILE_MASK) ||
!S_ISREG(inode->i_mode))
return -EINVAL;
if (((range.flags & F2FS_TRIM_FILE_DISCARD) &&
!f2fs_hw_support_discard(sbi)) ||
((range.flags & F2FS_TRIM_FILE_ZEROOUT) &&
IS_ENCRYPTED(inode) && f2fs_is_multi_device(sbi)))
return -EOPNOTSUPP;
file_start_write(filp);
inode_lock(inode);
if (f2fs_is_atomic_file(inode) || f2fs_compressed_file(inode) ||
range.start >= inode->i_size) {
ret = -EINVAL;
goto err;
}
if (range.len == 0)
goto err;
if (inode->i_size - range.start > range.len) {
end_addr = range.start + range.len;
} else {
end_addr = range.len == (u64)-1 ?
sbi->sb->s_maxbytes : inode->i_size;
to_end = true;
}
if (!IS_ALIGNED(range.start, F2FS_BLKSIZE) ||
(!to_end && !IS_ALIGNED(end_addr, F2FS_BLKSIZE))) {
ret = -EINVAL;
goto err;
}
index = F2FS_BYTES_TO_BLK(range.start);
pg_end = DIV_ROUND_UP(end_addr, F2FS_BLKSIZE);
ret = f2fs_convert_inline_inode(inode);
if (ret)
goto err;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
ret = filemap_write_and_wait_range(mapping, range.start,
to_end ? LLONG_MAX : end_addr - 1);
if (ret)
goto out;
truncate_inode_pages_range(mapping, range.start,
to_end ? -1 : end_addr - 1);
while (index < pg_end) {
struct dnode_of_data dn;
pgoff_t end_offset, count;
int i;
set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = f2fs_get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (ret) {
if (ret == -ENOENT) {
index = f2fs_get_next_page_offset(&dn, index);
continue;
}
goto out;
}
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
count = min(end_offset - dn.ofs_in_node, pg_end - index);
for (i = 0; i < count; i++, index++, dn.ofs_in_node++) {
struct block_device *cur_bdev;
block_t blkaddr = f2fs_data_blkaddr(&dn);
if (!__is_valid_data_blkaddr(blkaddr))
continue;
if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
DATA_GENERIC_ENHANCE)) {
ret = -EFSCORRUPTED;
f2fs_put_dnode(&dn);
goto out;
}
cur_bdev = f2fs_target_device(sbi, blkaddr, NULL);
if (f2fs_is_multi_device(sbi)) {
int di = f2fs_target_device_index(sbi, blkaddr);
blkaddr -= FDEV(di).start_blk;
}
if (len) {
if (prev_bdev == cur_bdev &&
index == prev_index + len &&
blkaddr == prev_block + len) {
len++;
} else {
ret = f2fs_secure_erase(prev_bdev,
inode, prev_index, prev_block,
len, range.flags);
if (ret) {
f2fs_put_dnode(&dn);
goto out;
}
len = 0;
}
}
if (!len) {
prev_bdev = cur_bdev;
prev_index = index;
prev_block = blkaddr;
len = 1;
}
}
f2fs_put_dnode(&dn);
if (fatal_signal_pending(current)) {
ret = -EINTR;
goto out;
}
cond_resched();
}
if (len)
ret = f2fs_secure_erase(prev_bdev, inode, prev_index,
prev_block, len, range.flags);
out:
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
err:
inode_unlock(inode);
file_end_write(filp);
return ret;
}
static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_comp_option option;
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
return -EOPNOTSUPP;
inode_lock_shared(inode);
if (!f2fs_compressed_file(inode)) {
inode_unlock_shared(inode);
return -ENODATA;
}
option.algorithm = F2FS_I(inode)->i_compress_algorithm;
option.log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
inode_unlock_shared(inode);
if (copy_to_user((struct f2fs_comp_option __user *)arg, &option,
sizeof(option)))
return -EFAULT;
return 0;
}
static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_comp_option option;
int ret = 0;
if (!f2fs_sb_has_compression(sbi))
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (copy_from_user(&option, (struct f2fs_comp_option __user *)arg,
sizeof(option)))
return -EFAULT;
if (!f2fs_compressed_file(inode) ||
option.log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
option.log_cluster_size > MAX_COMPRESS_LOG_SIZE ||
option.algorithm >= COMPRESS_MAX)
return -EINVAL;
file_start_write(filp);
inode_lock(inode);
if (f2fs_is_mmap_file(inode) || get_dirty_pages(inode)) {
ret = -EBUSY;
goto out;
}
if (inode->i_size != 0) {
ret = -EFBIG;
goto out;
}
F2FS_I(inode)->i_compress_algorithm = option.algorithm;
F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
F2FS_I(inode)->i_cluster_size = 1 << option.log_cluster_size;
f2fs_mark_inode_dirty_sync(inode, true);
if (!f2fs_is_compress_backend_ready(inode))
f2fs_warn(sbi, "compression algorithm is successfully set, "
"but current kernel doesn't support this algorithm.");
out:
inode_unlock(inode);
file_end_write(filp);
return ret;
}
static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, page_idx);
struct address_space *mapping = inode->i_mapping;
struct page *page;
pgoff_t redirty_idx = page_idx;
int i, page_len = 0, ret = 0;
page_cache_ra_unbounded(&ractl, len, 0);
for (i = 0; i < len; i++, page_idx++) {
page = read_cache_page(mapping, page_idx, NULL, NULL);
if (IS_ERR(page)) {
ret = PTR_ERR(page);
break;
}
page_len++;
}
for (i = 0; i < page_len; i++, redirty_idx++) {
page = find_lock_page(mapping, redirty_idx);
if (!page) {
ret = -ENOMEM;
break;
}
set_page_dirty(page);
f2fs_put_page(page, 1);
f2fs_put_page(page, 0);
}
return ret;
}
static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int blk_per_seg = sbi->blocks_per_seg;
int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!f2fs_compressed_file(inode))
return -EINVAL;
f2fs_balance_fs(F2FS_I_SB(inode), true);
file_start_write(filp);
inode_lock(inode);
if (!f2fs_is_compress_backend_ready(inode)) {
ret = -EOPNOTSUPP;
goto out;
}
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
ret = -EINVAL;
goto out;
}
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
goto out;
if (!atomic_read(&fi->i_compr_blocks))
goto out;
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
count = last_idx - page_idx;
while (count) {
int len = min(cluster_size, count);
ret = redirty_blocks(inode, page_idx, len);
if (ret < 0)
break;
if (get_dirty_pages(inode) >= blk_per_seg)
filemap_fdatawrite(inode->i_mapping);
count -= len;
page_idx += len;
}
if (!ret)
ret = filemap_write_and_wait_range(inode->i_mapping, 0,
LLONG_MAX);
if (ret)
f2fs_warn(sbi, "%s: The file might be partially decompressed (errno=%d). Please delete the file.",
__func__, ret);
out:
inode_unlock(inode);
file_end_write(filp);
return ret;
}
static int f2fs_ioc_compress_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int blk_per_seg = sbi->blocks_per_seg;
int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, ret;
if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
return -EOPNOTSUPP;
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
if (!f2fs_compressed_file(inode))
return -EINVAL;
f2fs_balance_fs(F2FS_I_SB(inode), true);
file_start_write(filp);
inode_lock(inode);
if (!f2fs_is_compress_backend_ready(inode)) {
ret = -EOPNOTSUPP;
goto out;
}
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
ret = -EINVAL;
goto out;
}
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
goto out;
set_inode_flag(inode, FI_ENABLE_COMPRESS);
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
count = last_idx - page_idx;
while (count) {
int len = min(cluster_size, count);
ret = redirty_blocks(inode, page_idx, len);
if (ret < 0)
break;
if (get_dirty_pages(inode) >= blk_per_seg)
filemap_fdatawrite(inode->i_mapping);
count -= len;
page_idx += len;
}
if (!ret)
ret = filemap_write_and_wait_range(inode->i_mapping, 0,
LLONG_MAX);
clear_inode_flag(inode, FI_ENABLE_COMPRESS);
if (ret)
f2fs_warn(sbi, "%s: The file might be partially compressed (errno=%d). Please delete the file.",
__func__, ret);
out:
inode_unlock(inode);
file_end_write(filp);
return ret;
}
static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case FS_IOC_GETFLAGS:
return f2fs_ioc_getflags(filp, arg);
case FS_IOC_SETFLAGS:
return f2fs_ioc_setflags(filp, arg);
case FS_IOC_GETVERSION:
return f2fs_ioc_getversion(filp, arg);
case F2FS_IOC_START_ATOMIC_WRITE:
return f2fs_ioc_start_atomic_write(filp);
case F2FS_IOC_COMMIT_ATOMIC_WRITE:
return f2fs_ioc_commit_atomic_write(filp);
case F2FS_IOC_START_VOLATILE_WRITE:
return f2fs_ioc_start_volatile_write(filp);
case F2FS_IOC_RELEASE_VOLATILE_WRITE:
return f2fs_ioc_release_volatile_write(filp);
case F2FS_IOC_ABORT_VOLATILE_WRITE:
return f2fs_ioc_abort_volatile_write(filp);
case F2FS_IOC_SHUTDOWN:
return f2fs_ioc_shutdown(filp, arg);
case FITRIM:
return f2fs_ioc_fitrim(filp, arg);
case FS_IOC_SET_ENCRYPTION_POLICY:
return f2fs_ioc_set_encryption_policy(filp, arg);
case FS_IOC_GET_ENCRYPTION_POLICY:
return f2fs_ioc_get_encryption_policy(filp, arg);
case FS_IOC_GET_ENCRYPTION_PWSALT:
return f2fs_ioc_get_encryption_pwsalt(filp, arg);
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
return f2fs_ioc_get_encryption_policy_ex(filp, arg);
case FS_IOC_ADD_ENCRYPTION_KEY:
return f2fs_ioc_add_encryption_key(filp, arg);
case FS_IOC_REMOVE_ENCRYPTION_KEY:
return f2fs_ioc_remove_encryption_key(filp, arg);
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
return f2fs_ioc_remove_encryption_key_all_users(filp, arg);
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
return f2fs_ioc_get_encryption_key_status(filp, arg);
case FS_IOC_GET_ENCRYPTION_NONCE:
return f2fs_ioc_get_encryption_nonce(filp, arg);
case F2FS_IOC_GARBAGE_COLLECT:
return f2fs_ioc_gc(filp, arg);
case F2FS_IOC_GARBAGE_COLLECT_RANGE:
return f2fs_ioc_gc_range(filp, arg);
case F2FS_IOC_WRITE_CHECKPOINT:
return f2fs_ioc_write_checkpoint(filp, arg);
case F2FS_IOC_DEFRAGMENT:
return f2fs_ioc_defragment(filp, arg);
case F2FS_IOC_MOVE_RANGE:
return f2fs_ioc_move_range(filp, arg);
case F2FS_IOC_FLUSH_DEVICE:
return f2fs_ioc_flush_device(filp, arg);
case F2FS_IOC_GET_FEATURES:
return f2fs_ioc_get_features(filp, arg);
case FS_IOC_FSGETXATTR:
return f2fs_ioc_fsgetxattr(filp, arg);
case FS_IOC_FSSETXATTR:
return f2fs_ioc_fssetxattr(filp, arg);
case F2FS_IOC_GET_PIN_FILE:
return f2fs_ioc_get_pin_file(filp, arg);
case F2FS_IOC_SET_PIN_FILE:
return f2fs_ioc_set_pin_file(filp, arg);
case F2FS_IOC_PRECACHE_EXTENTS:
return f2fs_ioc_precache_extents(filp, arg);
case F2FS_IOC_RESIZE_FS:
return f2fs_ioc_resize_fs(filp, arg);
case FS_IOC_ENABLE_VERITY:
return f2fs_ioc_enable_verity(filp, arg);
case FS_IOC_MEASURE_VERITY:
return f2fs_ioc_measure_verity(filp, arg);
case FS_IOC_READ_VERITY_METADATA:
return f2fs_ioc_read_verity_metadata(filp, arg);
case FS_IOC_GETFSLABEL:
return f2fs_ioc_getfslabel(filp, arg);
case FS_IOC_SETFSLABEL:
return f2fs_ioc_setfslabel(filp, arg);
case F2FS_IOC_GET_COMPRESS_BLOCKS:
return f2fs_get_compress_blocks(filp, arg);
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
return f2fs_release_compress_blocks(filp, arg);
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
return f2fs_reserve_compress_blocks(filp, arg);
case F2FS_IOC_SEC_TRIM_FILE:
return f2fs_sec_trim_file(filp, arg);
case F2FS_IOC_GET_COMPRESS_OPTION:
return f2fs_ioc_get_compress_option(filp, arg);
case F2FS_IOC_SET_COMPRESS_OPTION:
return f2fs_ioc_set_compress_option(filp, arg);
case F2FS_IOC_DECOMPRESS_FILE:
return f2fs_ioc_decompress_file(filp, arg);
case F2FS_IOC_COMPRESS_FILE:
return f2fs_ioc_compress_file(filp, arg);
default:
return -ENOTTY;
}
}
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
return -EIO;
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(file_inode(filp))))
return -ENOSPC;
return __f2fs_ioctl(filp, cmd, arg);
}
static ssize_t f2fs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
int ret;
if (!f2fs_is_compress_backend_ready(inode))
return -EOPNOTSUPP;
ret = generic_file_read_iter(iocb, iter);
if (ret > 0)
f2fs_update_iostat(F2FS_I_SB(inode), APP_READ_IO, ret);
return ret;
}
static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
ssize_t ret;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
ret = -EIO;
goto out;
}
if (!f2fs_is_compress_backend_ready(inode)) {
ret = -EOPNOTSUPP;
goto out;
}
if (iocb->ki_flags & IOCB_NOWAIT) {
if (!inode_trylock(inode)) {
ret = -EAGAIN;
goto out;
}
} else {
inode_lock(inode);
}
if (unlikely(IS_IMMUTABLE(inode))) {
ret = -EPERM;
goto unlock;
}
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
ret = -EPERM;
goto unlock;
}
ret = generic_write_checks(iocb, from);
if (ret > 0) {
bool preallocated = false;
size_t target_size = 0;
int err;
if (iov_iter_fault_in_readable(from, iov_iter_count(from)))
set_inode_flag(inode, FI_NO_PREALLOC);
if ((iocb->ki_flags & IOCB_NOWAIT)) {
if (!f2fs_overwrite_io(inode, iocb->ki_pos,
iov_iter_count(from)) ||
f2fs_has_inline_data(inode) ||
f2fs_force_buffered_io(inode, iocb, from)) {
clear_inode_flag(inode, FI_NO_PREALLOC);
inode_unlock(inode);
ret = -EAGAIN;
goto out;
}
goto write;
}
if (is_inode_flag_set(inode, FI_NO_PREALLOC))
goto write;
if (iocb->ki_flags & IOCB_DIRECT) {
/*
* Convert inline data for Direct I/O before entering
* f2fs_direct_IO().
*/
err = f2fs_convert_inline_inode(inode);
if (err)
goto out_err;
/*
* If force_buffere_io() is true, we have to allocate
* blocks all the time, since f2fs_direct_IO will fall
* back to buffered IO.
*/
if (!f2fs_force_buffered_io(inode, iocb, from) &&
allow_outplace_dio(inode, iocb, from))
goto write;
}
preallocated = true;
target_size = iocb->ki_pos + iov_iter_count(from);
err = f2fs_preallocate_blocks(iocb, from);
if (err) {
out_err:
clear_inode_flag(inode, FI_NO_PREALLOC);
inode_unlock(inode);
ret = err;
goto out;
}
write:
ret = __generic_file_write_iter(iocb, from);
clear_inode_flag(inode, FI_NO_PREALLOC);
/* if we couldn't write data, we should deallocate blocks. */
if (preallocated && i_size_read(inode) < target_size) {
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_down_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_truncate(inode);
f2fs_up_write(&F2FS_I(inode)->i_mmap_sem);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
}
if (ret > 0)
f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret);
}
unlock:
inode_unlock(inode);
out:
trace_f2fs_file_write_iter(inode, iocb->ki_pos,
iov_iter_count(from), ret);
if (ret > 0)
ret = generic_write_sync(iocb, ret);
return ret;
}
#ifdef CONFIG_COMPAT
struct compat_f2fs_gc_range {
u32 sync;
compat_u64 start;
compat_u64 len;
};
#define F2FS_IOC32_GARBAGE_COLLECT_RANGE _IOW(F2FS_IOCTL_MAGIC, 11,\
struct compat_f2fs_gc_range)
static int f2fs_compat_ioc_gc_range(struct file *file, unsigned long arg)
{
struct compat_f2fs_gc_range __user *urange;
struct f2fs_gc_range range;
int err;
urange = compat_ptr(arg);
err = get_user(range.sync, &urange->sync);
err |= get_user(range.start, &urange->start);
err |= get_user(range.len, &urange->len);
if (err)
return -EFAULT;
return __f2fs_ioc_gc_range(file, &range);
}
struct compat_f2fs_move_range {
u32 dst_fd;
compat_u64 pos_in;
compat_u64 pos_out;
compat_u64 len;
};
#define F2FS_IOC32_MOVE_RANGE _IOWR(F2FS_IOCTL_MAGIC, 9, \
struct compat_f2fs_move_range)
static int f2fs_compat_ioc_move_range(struct file *file, unsigned long arg)
{
struct compat_f2fs_move_range __user *urange;
struct f2fs_move_range range;
int err;
urange = compat_ptr(arg);
err = get_user(range.dst_fd, &urange->dst_fd);
err |= get_user(range.pos_in, &urange->pos_in);
err |= get_user(range.pos_out, &urange->pos_out);
err |= get_user(range.len, &urange->len);
if (err)
return -EFAULT;
return __f2fs_ioc_move_range(file, &range);
}
long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(file)))))
return -EIO;
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(file_inode(file))))
return -ENOSPC;
switch (cmd) {
case FS_IOC32_GETFLAGS:
cmd = FS_IOC_GETFLAGS;
break;
case FS_IOC32_SETFLAGS:
cmd = FS_IOC_SETFLAGS;
break;
case FS_IOC32_GETVERSION:
cmd = FS_IOC_GETVERSION;
break;
case F2FS_IOC32_GARBAGE_COLLECT_RANGE:
return f2fs_compat_ioc_gc_range(file, arg);
case F2FS_IOC32_MOVE_RANGE:
return f2fs_compat_ioc_move_range(file, arg);
case F2FS_IOC_START_ATOMIC_WRITE:
case F2FS_IOC_COMMIT_ATOMIC_WRITE:
case F2FS_IOC_START_VOLATILE_WRITE:
case F2FS_IOC_RELEASE_VOLATILE_WRITE:
case F2FS_IOC_ABORT_VOLATILE_WRITE:
case F2FS_IOC_SHUTDOWN:
case FITRIM:
case FS_IOC_SET_ENCRYPTION_POLICY:
case FS_IOC_GET_ENCRYPTION_PWSALT:
case FS_IOC_GET_ENCRYPTION_POLICY:
case FS_IOC_GET_ENCRYPTION_POLICY_EX:
case FS_IOC_ADD_ENCRYPTION_KEY:
case FS_IOC_REMOVE_ENCRYPTION_KEY:
case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
case FS_IOC_GET_ENCRYPTION_NONCE:
case F2FS_IOC_GARBAGE_COLLECT:
case F2FS_IOC_WRITE_CHECKPOINT:
case F2FS_IOC_DEFRAGMENT:
case F2FS_IOC_FLUSH_DEVICE:
case F2FS_IOC_GET_FEATURES:
case FS_IOC_FSGETXATTR:
case FS_IOC_FSSETXATTR:
case F2FS_IOC_GET_PIN_FILE:
case F2FS_IOC_SET_PIN_FILE:
case F2FS_IOC_PRECACHE_EXTENTS:
case F2FS_IOC_RESIZE_FS:
case FS_IOC_ENABLE_VERITY:
case FS_IOC_MEASURE_VERITY:
case FS_IOC_READ_VERITY_METADATA:
case FS_IOC_GETFSLABEL:
case FS_IOC_SETFSLABEL:
case F2FS_IOC_GET_COMPRESS_BLOCKS:
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
case F2FS_IOC_SEC_TRIM_FILE:
case F2FS_IOC_GET_COMPRESS_OPTION:
case F2FS_IOC_SET_COMPRESS_OPTION:
case F2FS_IOC_DECOMPRESS_FILE:
case F2FS_IOC_COMPRESS_FILE:
break;
default:
return -ENOIOCTLCMD;
}
return __f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
}
#endif
const struct file_operations f2fs_file_operations = {
.llseek = f2fs_llseek,
.read_iter = f2fs_file_read_iter,
.write_iter = f2fs_file_write_iter,
.open = f2fs_file_open,
.release = f2fs_release_file,
.mmap = f2fs_file_mmap,
.flush = f2fs_file_flush,
.fsync = f2fs_sync_file,
.fallocate = f2fs_fallocate,
.unlocked_ioctl = f2fs_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = f2fs_compat_ioctl,
#endif
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
};