我想在Golang中运行clonefile→尝试发出系统调用

首先,

去年年底(2018年末),在我的工作场所也有人谈论Mac的cp命令的选项。
最近的macOS据说可以在瞬间复制文件。

在APFS(Apple文件系统)中,如果在cp命令中指定-c选项,它将使用clonefile系统调用,因此可以在瞬间内复制几GB级别的文件。

然后,我想要用Golang编写一个使用clonefile函数进行文件复制的工具。

特别是有经验的人可以直接访问链接:https://github.com/n-someya/goclonefile。

在Golang中用于发出系统调用的软件包

在Golang中,系统调用发行的包有两个存在着历史原因。
syscall包是Golang的标准包,提供了用于发行系统调用的功能。
然而,由于一系列问题,如对不同操作系统的特定支持等,目前功能的增加已经转移到了golang.org/x/sys包中。

建议查看 golang.org/x/sys/unix 包,因为 clonefile 系统调用是 macOS 的特有功能(macOS 是基于 Unix 的)。

在Unix系统中,每个系统调用都被分配了一个特定的编号。指定了每个系统调用被分配的编号的文件是zsysnum_darwin_amd64.go。

const (
//中略
    SYS_CLONEFILEAT                    = 462
//中略
)

在其中还记载了clonefile系统调用的号码。
这听起来很有希望。

使用上述的常量,实际上发起了系统调用的文件为zsyscall_darwin_amd64.1_11.go。

然而,无论在哪里都找不到使用SYS_CLONEFILEAT的部分…
看起来没有实施…

用Go语言实现clonefile系统调用的发出处理

因为没有办法,只能自己来实现。

根据Apple的说法,clonefile系统调用(准确地说是clonefileat系统调用)接受5个参数。

引数パラメータ1第2引数が相対パスの場合のカレントディレクトリのディスクリプタ2コピーファイルのパス3第4引数が相対パスの場合のカレントディレクトリのディスクリプタ4コピーファイルのパス5フラグ(現状は、NOFOLLOW: シンボリックリンクのリンク元までは追わないのみ?)

就Unix包中的系统调用发起函数而言,有三个函数:Syscall()接受三个参数,Syscall6()接受六个参数,Syscall9()接受九个参数。因此,我们可以将第六个参数设为0,调用Syscall6()应该是一个好的选择。

最终结果代码如下。

var (
    // Flags
    CLONE_NOFOLLOW    CLONEFILE_FLAG = 0x0001 /* Don't follow symbolic links */
    CLONE_NOOWNERCOPY CLONEFILE_FLAG = 0x0002 /* Don't copy ownership information from source */
    AT_FDCWD                         = -2
)

// Clonefile clonefile is fast copy method for darwin(MacOS)
func Clonefile(src, dst string) (err error) {
    if runtime.GOOS != OS_DARWIN {
        return fmt.Errorf("Clonefile is implemented for macOS")
    }
    var _p0, _p1 *byte
    _p0, err = unix.BytePtrFromString(src)
    if err != nil {
        return
    }
    _p1, err = unix.BytePtrFromString(dst)
    if err != nil {
        return
    }
    _, _, e1 := unix.Syscall6(unix.SYS_CLONEFILEAT, uintptr(AT_FDCWD), uintptr(unsafe.Pointer(_p0)), uintptr(AT_FDCWD), uintptr(unsafe.Pointer(_p1)), uintptr(CLONE_NOFOLLOW), 0)
    if e1 != 0 {
        err = errnoErr(e1)
    }
    return
}

各种不同的补充说明

【额外附加1】

从Go 1.12开始,在macOS上,系统调用的实现似乎发生了变化。
系统调用的函数现在是小写字母+下划线的形式,例如syscall_syscall6,并且系统调用的编号也是通过从libc获取的方式获得。
https://github.com/golang/sys/blob/9fbf701fc3/unix/zsyscall_darwin_arm64.go
(为什么会这样呢)

【附加2】

或许作为规范,可以直接调用C语言函数,而不是直接调用系统调用,就像这位的做法一样:https://github.com/go-darwin/apfs/blob/master/clone.go

Go Binary Hacks – syscall和golang.org/x/sys/ #golang以及Google文档中涵盖了有关这方面的详细信息。
广告
将在 10 秒后关闭
bannerAds