From 0ec206ad67d32762406adc19807b4952a0cc68e1 Mon Sep 17 00:00:00 2001 From: Ben McClelland Date: Mon, 13 Apr 2026 17:03:23 -0700 Subject: [PATCH] add PunchHole() to convert offline range to sparse PunchHole is a limited and specific version of hole punching. It's an archive layer operation that only converts unmapped offline extents into sparse extents. It is intended to be used when restoring sparse files after the initial creation set the entire file size offline. The offset and len fields are in units of bytes and must be aligned to the small (4KiB) block size. All regions of offline extents covered by the region will be converted into sparse online extents, including regions that straddle the boundaries of the region. Any existing sparse extents in the region are ignored. The data_version must match the inode or EINVAL is returned. The data_version is not modified by this operation. --- c_defs_linux.go | 3 +++ scoutfs.go | 33 ++++++++++++++++++++++++++++----- scoutfsdefs.go | 7 +++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/c_defs_linux.go b/c_defs_linux.go index 0540772..561616c 100644 --- a/c_defs_linux.go +++ b/c_defs_linux.go @@ -72,6 +72,7 @@ package scoutfs // typedef struct scoutfs_ioctl_xattr_index_entry scoutfs_ioctl_xattr_index_entry_t; // typedef struct scoutfs_ioctl_read_xattr_index scoutfs_ioctl_read_xattr_index_t; // typedef struct scoutfs_ioctl_inode_attr_x scoutfs_ioctl_inode_attr_x_t; +// typedef struct scoutfs_ioctl_punch_offline scoutfs_ioctl_punch_offline_t; import "C" const IOCQUERYINODES = C.SCOUTFS_IOC_WALK_INODES @@ -122,6 +123,7 @@ const IOCIAXPROJECTID = C.SCOUTFS_IOC_IAX_PROJECT_ID const IOCIAXBITS = C.SCOUTFS_IOC_IAX__BITS const IOCGETATTRX = C.SCOUTFS_IOC_GET_ATTR_X const IOCSETATTRX = C.SCOUTFS_IOC_SET_ATTR_X +const IOCPUNCHOFFLINE = C.SCOUTFS_IOC_PUNCH_OFFLINE type InodesEntry C.scoutfs_ioctl_walk_inodes_entry_t type queryInodes C.scoutfs_ioctl_walk_inodes_t @@ -148,6 +150,7 @@ type getQuotaRules C.scoutfs_ioctl_get_quota_rules_t type indexEntry C.scoutfs_ioctl_xattr_index_entry_t type readXattrIndex C.scoutfs_ioctl_read_xattr_index_t type inodeAttrX C.scoutfs_ioctl_inode_attr_x_t +type punchOffline C.scoutfs_ioctl_punch_offline_t const sizeofstatfsMore = C.sizeof_scoutfs_ioctl_statfs_more_t const sizeofxattrTotal = C.sizeof_scoutfs_ioctl_xattr_total_t diff --git a/scoutfs.go b/scoutfs.go index 131e5ab..74ba189 100644 --- a/scoutfs.go +++ b/scoutfs.go @@ -905,11 +905,7 @@ func StageMoveAt(from, to *os.File, len, fromOffset, toOffset, version uint64) e } _, err := scoutfsctl(to, IOCMOVEBLOCKS, unsafe.Pointer(&mb)) - if err != nil { - return err - } - - return nil + return err } // XattrTotal has the total values matching id triple @@ -1807,3 +1803,30 @@ func (i indexEntry) increment() indexEntry { } return i } + +// PunchHole is a limited and specific version of hole punching. It's an +// archive layer operation that only converts unmapped offline extents +// into sparse extents. It is intended to be used when restoring sparse +// files after the initial creation set the entire file size offline. +// +// The offset and len fields are in units of bytes and must be aligned +// to the small (4KiB) block size. All regions of offline extents +// covered by the region will be converted into sparse online extents, +// including regions that straddle the boundaries of the region. Any +// existing sparse extents in the region are ignored. +// +// The data_version must match the inode or EINVAL is returned. The +// data_version is not modified by this operation. +// +// EINVAL is returned if any mapped extents are found in the region. If +// an error is returned then partial progress may have been made. +func PunchHole(f *os.File, length, offset, version uint64) error { + po := punchOffline{ + Offset: offset, + Len: length, + Version: version, + } + + _, err := scoutfsctl(f, IOCPUNCHOFFLINE, unsafe.Pointer(&po)) + return err +} diff --git a/scoutfsdefs.go b/scoutfsdefs.go index b280246..552edb7 100644 --- a/scoutfsdefs.go +++ b/scoutfsdefs.go @@ -51,6 +51,7 @@ const IOCIAXPROJECTID = 0x200 const IOCIAXBITS = 0x100 const IOCGETATTRX = 0x4068e812 const IOCSETATTRX = 0x4068e813 +const IOCPUNCHOFFLINE = 0x4020e818 type InodesEntry struct { Major uint64 @@ -241,6 +242,12 @@ type inodeAttrX struct { Bits uint64 Project_id uint64 } +type punchOffline struct { + Offset uint64 + Len uint64 + Version uint64 + Flags uint64 +} const sizeofstatfsMore = 0x30 const sizeofxattrTotal = 0x28