├── go.sum ├── testdata └── testfile.txt ├── go.mod ├── hack ├── boilerplate │ └── boilerplate.go.txt └── make │ └── go.mk ├── docs ├── README.md └── apfs.md ├── apfs.go ├── .codecov.yml ├── Makefile ├── .gitignore ├── cmd └── go-apfs │ └── main.go ├── example_test.go ├── LICENSE ├── .circleci └── config.yml ├── .golangci.yml ├── rename_test.go ├── rename.go ├── all_test.go ├── clone_test.go ├── README.md ├── copy_test.go ├── clone.go └── copy.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/testfile.txt: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-darwin/apfs 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Go Darwin Authors 2 | // SPDX-License-Identifier: BSD-3-Clause 3 | 4 | //go:build darwin && amd64 5 | // +build darwin,amd64 6 | 7 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | 3 | ## References 4 | 5 | - [About Apple File System | Apple Developer Documentation](https://developer.apple.com/documentation/foundation/file_system/about_apple_file_system) 6 | -------------------------------------------------------------------------------- /apfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | // Package apfs implements an Apple File System(APFS) bindings for Go. 8 | package apfs // import "github.com/go-darwin/apfs" 9 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 1 3 | round: down 4 | range: "70...100" 5 | 6 | status: 7 | project: 8 | default: 9 | target: auto 10 | threshold: 10% 11 | patch: 12 | default: 13 | only_pulls: true 14 | target: 50% 15 | threshold: 10% 16 | ignore: 17 | - "vendor" 18 | 19 | comment: 20 | behavior: default 21 | require_changes: true 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # global 3 | 4 | .DEFAULT_GOAL = test 5 | 6 | # ---------------------------------------------------------------------------- 7 | # target 8 | 9 | # ---------------------------------------------------------------------------- 10 | # include 11 | 12 | include hack/make/go.mk 13 | 14 | # ---------------------------------------------------------------------------- 15 | # overlays 16 | 17 | CGO_ENABLED = 1 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # apfs project generated files to ignore 2 | # if you want to ignore files created by your editor/tools, 3 | # please consider a global .gitignore https://help.github.com/articles/ignoring-files 4 | # please do not open a pull request to add something created by your editor or tools 5 | # 6 | # github/gitignore/Go.gitignore 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, build with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | -------------------------------------------------------------------------------- /cmd/go-apfs/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Koichi Shiraishi. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "log" 13 | "os" 14 | 15 | "github.com/go-darwin/apfs" 16 | ) 17 | 18 | func main() { 19 | flag.Usage = func() { 20 | fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) 21 | flag.PrintDefaults() 22 | } 23 | flag.Parse() 24 | if flag.NArg() != 2 { 25 | flag.Usage() 26 | os.Exit(1) 27 | } 28 | 29 | state := apfs.CopyFileStateAlloc() 30 | defer func() { 31 | if err := apfs.CopyFileStateFree(state); err != nil { 32 | log.Fatal(err) 33 | } 34 | }() 35 | 36 | src, dst := flag.Arg(0), flag.Arg(1) 37 | cloned, err := apfs.CopyFile(src, dst, state, apfs.COPYFILE_CLONE) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | fmt.Printf("isCloned: %v", cloned) 42 | } 43 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "os" 13 | ) 14 | 15 | func ExampleCopyFile() { 16 | src, dst := os.Args[1], os.Args[2] 17 | 18 | state := CopyFileStateAlloc() 19 | defer func() { 20 | if err := CopyFileStateFree(state); err != nil { 21 | log.Fatal(err) 22 | } 23 | }() 24 | 25 | cloned, err := CopyFile(src, dst, state, COPYFILE_CLONE) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Printf("isCloned: %v", cloned) 30 | 31 | // Outpt: true // or false 32 | } 33 | 34 | func ExampleFcopyFile() {} 35 | func ExampleCopyFileStateAlloc() {} 36 | func ExampleCopyFileStateFree() {} 37 | func ExampleCopyFileStateGet() {} 38 | 39 | func ExampleCloneFile() { 40 | src, dst := os.Args[1], os.Args[2] 41 | err := CloneFile(src, dst, CLONEFILE_FLAG(0)) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | } 46 | 47 | func ExampleCloneFileAt() {} 48 | func ExampleFcloneFileAt() {} 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, The go-darwin Authors. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | golang: cci-orb/golang@volatile 5 | codecov: codecov/codecov@volatile 6 | 7 | darwin: &darwin 8 | executor: golang/macos 9 | working_directory: ~/go/src/github.com/go-darwin/apfs 10 | 11 | jobs: 12 | test: 13 | <<: *darwin 14 | steps: 15 | - run: 16 | name: Show versions 17 | command: | 18 | uname -a 19 | sw_vers 20 | xcodebuild -version 21 | system_profiler SPHardwareDataType 22 | - checkout 23 | - golang/homebrew 24 | - golang/gomod: 25 | file: "go.mod" 26 | paths: "~/go/pkg/mod" 27 | - run: 28 | name: Test and collect coverages 29 | environment: 30 | GO111MODULE: "on" 31 | command: | 32 | make coverage/ci 33 | - codecov/upload: 34 | file: "/tmp/ci/artifacts/coverage.out" 35 | flags: darwin 36 | - store_artifacts: 37 | path: /tmp/ci/artifacts 38 | - store_artifacts: 39 | path: /tmp/ci/artifacts 40 | - store_test_results: 41 | path: /tmp/ci/test-results 42 | 43 | lint: 44 | <<: *darwin 45 | steps: 46 | - checkout 47 | - golang/homebrew 48 | - golang/gomod: 49 | file: "go.mod" 50 | paths: "~/go/pkg/mod" 51 | - run: 52 | name: Run lint for sources 53 | command: | 54 | make lint 55 | 56 | workflows: 57 | version: 2 58 | workflows: 59 | jobs: 60 | - test: 61 | context: org-global 62 | - lint: 63 | context: org-global 64 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | concurrency: 12 3 | deadline: 5m 4 | issues-exit-code: 1 5 | tests: true 6 | build-tags: 7 | - darwin 8 | skip-dirs: 9 | - "cmd" 10 | skip-files: 11 | - ".*\\.pb\\.go" 12 | - ".*(.|_)gen\\.go" 13 | modules-download-mode: readonly 14 | 15 | linters-settings: 16 | errcheck: 17 | check-type-assertions: true 18 | check-blank: true 19 | # exclude: .errcheckignore 20 | goconst: 21 | min-len: 3 22 | min-occurrences: 3 23 | gofmt: 24 | simplify: true 25 | goimports: 26 | local-prefixes: github.com/go-darwin/apfs 27 | golint: 28 | min-confidence: 0.3 29 | gocritic: 30 | enabled-tags: 31 | - diagnostic 32 | - style 33 | - performance 34 | - experimental 35 | - opinionated 36 | disabled-checks: 37 | - dupImport 38 | maligned: 39 | suggest-new: true 40 | misspell: 41 | locale: US 42 | nakedret: 43 | max-func-lines: 30 44 | prealloc: 45 | simple: true 46 | range-loops: true 47 | for-loops: false 48 | unparam: 49 | algo: cha 50 | check-exported: true 51 | unused: 52 | check-exported: false 53 | 54 | linters: 55 | enable: 56 | enable-all: true 57 | disable: 58 | - gochecknoglobals 59 | - gochecknoinits 60 | - gocyclo 61 | - golint 62 | - gosec 63 | - govet 64 | - lll 65 | - nakedret 66 | - scopelint 67 | fast: true 68 | 69 | issues: 70 | exclude-rules: 71 | - path: "_test.go" 72 | linters: 73 | - dupl 74 | - errcheck 75 | - gocritic 76 | 77 | output: 78 | format: colored-line-number 79 | print-issued-lines: true 80 | print-linter-name: true 81 | -------------------------------------------------------------------------------- /rename_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | ) 14 | 15 | func TestRenamexNp(t *testing.T) { 16 | type args struct { 17 | src string 18 | dst string 19 | flag RENAME_FALG 20 | } 21 | tests := []struct { 22 | name string 23 | args args 24 | wantErr bool 25 | }{ 26 | { 27 | name: "simple", 28 | args: args{ 29 | src: filepath.Join(mountPoint, "test-renamexnp.txt"), 30 | dst: filepath.Join(mountPoint, "renamexnp.txt"), 31 | flag: RENAME_FALG(0), 32 | }, 33 | wantErr: false, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | fi, err := os.Create(tt.args.src) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | defer fi.Close() 43 | 44 | err = RenamexNp(tt.args.src, tt.args.dst, tt.args.flag) 45 | if (err != nil && fi.Name() == tt.args.dst) != tt.wantErr { 46 | t.Errorf("RenamexNp(%v, %v, %v) error = %v, gotPath %v, wantErr %v", tt.args.src, tt.args.dst, tt.args.flag, err, fi.Name(), tt.wantErr) 47 | } 48 | }) 49 | } 50 | } 51 | 52 | func TestRenameatxNp(t *testing.T) { 53 | type args struct { 54 | src string 55 | dst string 56 | flag RENAME_FALG 57 | } 58 | tests := []struct { 59 | name string 60 | args args 61 | wantErr bool 62 | }{ 63 | { 64 | name: "simple", 65 | args: args{ 66 | src: filepath.Join(mountPoint, "test-renameatxnp.txt"), 67 | dst: filepath.Join(mountPoint, "renameatxnp.txt"), 68 | flag: RENAME_FALG(0), 69 | }, 70 | wantErr: false, 71 | }, 72 | } 73 | for _, tt := range tests { 74 | t.Run(tt.name, func(t *testing.T) { 75 | fi, err := os.Create(tt.args.src) 76 | if err != nil { 77 | t.Fatal(err) 78 | } 79 | defer fi.Close() 80 | 81 | err = RenameatxNp(tt.args.src, tt.args.dst, tt.args.flag) 82 | if (err != nil && fi.Name() == tt.args.dst) != tt.wantErr { 83 | t.Errorf("RenameatxNp(%v, %v, %v) error = %v, gotPath %v,, wantErr %v", tt.args.src, tt.args.dst, tt.args.flag, err, fi.Name(), tt.wantErr) 84 | } 85 | }) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rename.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | /* 10 | #include // for AT_FDCWD 11 | #include 12 | */ 13 | import "C" 14 | import ( 15 | "fmt" 16 | "path/filepath" 17 | "syscall" 18 | ) 19 | 20 | // RENAME_FALG provides the rename flag. 21 | type RENAME_FALG uint 22 | 23 | var ( 24 | // RENAME_SWAP on file systems that support it (see getattrlist(2) VOL_CAP_INT_RENAME_SWAP), it will cause the source and target to be atomically swapped. 25 | // 26 | // Source and target need not be of the same type, i.e. it is possible to swap a file with a directory. 27 | // 28 | // EINVAL is returned in case of bitwise-inclusive OR with RENAME_EXCL. 29 | RENAME_SWAP RENAME_FALG = 0x00000002 30 | // RENAME_EXCL on file systems that support it (see getattrlist(2) VOL_CAP_INT_RENAME_EXCL), it will cause EEXIST to be returned if the destination already exists. 31 | // 32 | // EINVAL is returned in case of bitwise-inclusive OR with RENAME_SWAP. 33 | RENAME_EXCL RENAME_FALG = 0x00000004 34 | ) 35 | 36 | // RenamexNp system calls are similar to rename() and renameat() counterparts except that they take a flags argument. 37 | // int 38 | // renamex_np(const char *from, const char *to, unsigned int flags); 39 | func RenamexNp(src, dst string, flag RENAME_FALG) error { 40 | if err := C.renamex_np(C.CString(src), C.CString(dst), C.unsigned(flag)); err != 0 { 41 | return fmt.Errorf("error: C.renamex_np: %v", (syscall.Errno(err))) 42 | } 43 | 44 | return nil 45 | } 46 | 47 | // RenameatxNp system calls are similar to rename() and renameat() counterparts except that they take a flags argument. 48 | // int 49 | // renameatx_np(int fromfd, const char *from, int tofd, const char *to, unsigned int flags); 50 | func RenameatxNp(src, dst string, flag RENAME_FALG) error { 51 | var srcFd C.int 52 | if !filepath.IsAbs(src) { 53 | srcFd = C.AT_FDCWD 54 | } 55 | var dstFd C.int 56 | if !filepath.IsAbs(dst) { 57 | dstFd = C.AT_FDCWD 58 | } 59 | if err := C.renameatx_np(srcFd, C.CString(src), dstFd, C.CString(dst), C.unsigned(flag)); err != 0 { 60 | return fmt.Errorf("error: C.renameatx_np: %v", syscall.Errno(err)) 61 | } 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /all_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | import ( 10 | "io" 11 | "log" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "testing" 16 | ) 17 | 18 | var ( 19 | sparseImage = filepath.Join("testdata", "apfs.sparseimage") 20 | mountPoint = filepath.Join("testdata", "apfs") 21 | testFileGolden = filepath.Join("testdata", "testfile.txt") 22 | testFile = filepath.Join(mountPoint, "testfile.txt") 23 | ) 24 | 25 | func setupTest() { 26 | log.SetFlags(log.Lshortfile) 27 | 28 | if err := createAPFSSparseImage(); err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | if err := mountAPFSSparseImage(); err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | if err := copyFile(testFile, testFileGolden); err != nil { 37 | log.Fatal(err) 38 | } 39 | } 40 | 41 | func cleanupTest() { 42 | if err := unmountAPFSSparseImage(); err != nil { 43 | log.Fatal(err) 44 | } 45 | if err := os.Remove(sparseImage); err != nil { 46 | log.Fatal(err) 47 | } 48 | } 49 | 50 | func TestMain(m *testing.M) { 51 | setupTest() 52 | err := m.Run() 53 | cleanupTest() 54 | os.Exit(err) 55 | } 56 | 57 | var hdiutl = filepath.Join("/usr", "bin", "hdiutil") 58 | 59 | func createAPFSSparseImage() error { 60 | cmd := exec.Command(hdiutl, []string{"create", "-fs", "-quiet", "APFS", "-size", "1GB", sparseImage}...) 61 | if err := cmd.Run(); err != nil { 62 | return err 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func mountAPFSSparseImage() error { 69 | cmd := exec.Command(hdiutl, []string{"mount", "-mountpoint", mountPoint, sparseImage}...) 70 | if err := cmd.Run(); err != nil { 71 | return err 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func unmountAPFSSparseImage() error { 78 | cmd := exec.Command(hdiutl, []string{"unmount", mountPoint}...) 79 | if err := cmd.Run(); err != nil { 80 | return err 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // copyFile copies a file from src to dst. 87 | func copyFile(dst, src string) error { 88 | s, err := os.Open(src) 89 | if err != nil { 90 | return err 91 | } 92 | defer func() { 93 | e := s.Close() 94 | if err == nil { 95 | err = e 96 | } 97 | }() 98 | 99 | d, err := os.Create(dst) 100 | if err != nil { 101 | return err 102 | } 103 | defer func() { 104 | e := d.Close() 105 | if err == nil { 106 | err = e 107 | } 108 | }() 109 | 110 | _, err = io.Copy(d, s) 111 | if err != nil { 112 | return err 113 | } 114 | 115 | i, err := os.Stat(src) 116 | if err != nil { 117 | return err 118 | } 119 | 120 | return os.Chmod(dst, i.Mode()) 121 | } 122 | -------------------------------------------------------------------------------- /clone_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | ) 14 | 15 | func TestCloneFile(t *testing.T) { 16 | type args struct { 17 | src string 18 | dst string 19 | flag CLONEFILE_FLAG 20 | } 21 | tests := []struct { 22 | name string 23 | args args 24 | wantErr bool 25 | }{ 26 | { 27 | name: "simple", 28 | args: args{ 29 | src: testFile, 30 | dst: filepath.Join(mountPoint, "clonefile.txt"), 31 | flag: CLONEFILE_FLAG(0), 32 | }, 33 | wantErr: false, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | err := CloneFile(tt.args.src, tt.args.dst, tt.args.flag) 39 | _, statErr := os.Stat(tt.args.dst) 40 | if (err != nil && statErr != nil) != tt.wantErr { 41 | t.Errorf("CloneFile(%v, %v, %v) error = %v, wantErr %v", tt.args.src, tt.args.dst, tt.args.flag, err, tt.wantErr) 42 | } 43 | }) 44 | } 45 | } 46 | 47 | func TestCloneFileAt(t *testing.T) { 48 | type args struct { 49 | src string 50 | dst string 51 | flag CLONEFILE_FLAG 52 | } 53 | tests := []struct { 54 | name string 55 | args args 56 | wantErr bool 57 | }{ 58 | { 59 | name: "simple", 60 | args: args{ 61 | src: testFile, 62 | dst: filepath.Join(mountPoint, "clonefileat.txt"), 63 | flag: CLONEFILE_FLAG(0), 64 | }, 65 | wantErr: false, 66 | }, 67 | } 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | if err := CloneFileAt(tt.args.src, tt.args.dst, tt.args.flag); (err != nil) != tt.wantErr { 71 | t.Errorf("CloneFileAt(%v, %v, %v) error = %v, wantErr %v", tt.args.src, tt.args.dst, tt.args.flag, err, tt.wantErr) 72 | } 73 | }) 74 | } 75 | } 76 | 77 | func TestFcloneFileAt(t *testing.T) { 78 | type args struct { 79 | srcFd uintptr 80 | dst string 81 | flag CLONEFILE_FLAG 82 | } 83 | tests := []struct { 84 | name string 85 | file string 86 | args args 87 | wantErr bool 88 | }{ 89 | { 90 | name: "simple", 91 | args: args{ 92 | dst: filepath.Join(mountPoint, "fclonefileat.txt"), 93 | flag: CLONEFILE_FLAG(0), 94 | }, 95 | file: testFile, 96 | wantErr: false, 97 | }, 98 | } 99 | for _, tt := range tests { 100 | fi, err := os.Open(tt.file) 101 | if err != nil { 102 | t.Fatal(err) 103 | } 104 | defer fi.Close() 105 | 106 | tt.args.srcFd = fi.Fd() 107 | t.Run(tt.name, func(t *testing.T) { 108 | if err := FcloneFileAt(tt.args.srcFd, tt.args.dst, tt.args.flag); (err != nil) != tt.wantErr { 109 | t.Errorf("FcloneFileAt(%v, %v, %v) error = %v, wantErr %v", tt.args.srcFd, tt.args.dst, tt.args.flag, err, tt.wantErr) 110 | } 111 | }) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apfs 2 | 3 | [![CircleCI][circleci-badge]][circleci] [![godoc.org][godoc-badge]][godoc] [![codecov.io][codecov-badge]][codecov] [![Releases][release-badge]][release] [![GA][ga-badge]][ga] 4 | 5 | Package apfs implements an Apple File System(apfs) bindings for Go. 6 | 7 | 8 | ## Install 9 | 10 | Installing `apfs` package: 11 | 12 | ```sh 13 | go get -u -v github.com/go-darwin/apfs 14 | ``` 15 | 16 | 17 | ## License 18 | 19 | The apfs is released under the [BSD 3-Clause License][license-bsd-3-clause]. 20 | 21 | 22 | 23 | [license-bsd-3-clause]: https://opensource.org/licenses/BSD-3-Clause 24 | 25 | 26 | [circleci]: https://circleci.com/gh/go-darwin/workflows/apfs 27 | [godoc]: https://godoc.org/github.com/go-darwin/apfs 28 | [codecov]: https://codecov.io/gh/go-darwin/apfs 29 | [release]: https://github.com/go-darwin/apfs/releases 30 | [ga]: https://github.com/go-darwin/apfs 31 | 32 | [circleci-badge]: https://img.shields.io/circleci/project/github/go-darwin/apfs/master.svg?style=for-the-badge&label=CIRCLECI&logo=circleci 33 | [godoc-badge]: https://img.shields.io/badge/godoc-reference-4F73B3.svg?style=for-the-badge&label=GODOC.ORG&logoWidth=25&logo=data%3Aimage%2Fsvg%2Bxml%3Bcharset%3Dutf-8%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iODUgNTUgMTIwIDEyMCI+PHBhdGggZmlsbD0iIzJEQkNBRiIgZD0iTTQwLjIgMTAxLjFjLS40IDAtLjUtLjItLjMtLjVsMi4xLTIuN2MuMi0uMy43LS41IDEuMS0uNWgzNS43Yy40IDAgLjUuMy4zLjZsLTEuNyAyLjZjLS4yLjMtLjcuNi0xIC42bC0zNi4yLS4xek0yNS4xIDExMC4zYy0uNCAwLS41LS4yLS4zLS41bDIuMS0yLjdjLjItLjMuNy0uNSAxLjEtLjVoNDUuNmMuNCAwIC42LjMuNS42bC0uOCAyLjRjLS4xLjQtLjUuNi0uOS42bC00Ny4zLjF6TTQ5LjMgMTE5LjVjLS40IDAtLjUtLjMtLjMtLjZsMS40LTIuNWMuMi0uMy42LS42IDEtLjZoMjBjLjQgMCAuNi4zLjYuN2wtLjIgMi40YzAgLjQtLjQuNy0uNy43bC0yMS44LS4xek0xNTMuMSA5OS4zYy02LjMgMS42LTEwLjYgMi44LTE2LjggNC40LTEuNS40LTEuNi41LTIuOS0xLTEuNS0xLjctMi42LTIuOC00LjctMy44LTYuMy0zLjEtMTIuNC0yLjItMTguMSAxLjUtNi44IDQuNC0xMC4zIDEwLjktMTAuMiAxOSAuMSA4IDUuNiAxNC42IDEzLjUgMTUuNyA2LjguOSAxMi41LTEuNSAxNy02LjYuOS0xLjEgMS43LTIuMyAyLjctMy43aC0xOS4zYy0yLjEgMC0yLjYtMS4zLTEuOS0zIDEuMy0zLjEgMy43LTguMyA1LjEtMTAuOS4zLS42IDEtMS42IDIuNS0xLjZoMzYuNGMtLjIgMi43LS4yIDUuNC0uNiA4LjEtMS4xIDcuMi0zLjggMTMuOC04LjIgMTkuNi03LjIgOS41LTE2LjYgMTUuNC0yOC41IDE3LTkuOCAxLjMtMTguOS0uNi0yNi45LTYuNi03LjQtNS42LTExLjYtMTMtMTIuNy0yMi4yLTEuMy0xMC45IDEuOS0yMC43IDguNS0yOS4zIDcuMS05LjMgMTYuNS0xNS4yIDI4LTE3LjMgOS40LTEuNyAxOC40LS42IDI2LjUgNC45IDUuMyAzLjUgOS4xIDguMyAxMS42IDE0LjEuNi45LjIgMS40LTEgMS43eiIvPjxwYXRoIGZpbGw9IiMyREJDQUYiIGQ9Ik0xODYuMiAxNTQuNmMtOS4xLS4yLTE3LjQtMi44LTI0LjQtOC44LTUuOS01LjEtOS42LTExLjYtMTAuOC0xOS4zLTEuOC0xMS4zIDEuMy0yMS4zIDguMS0zMC4yIDcuMy05LjYgMTYuMS0xNC42IDI4LTE2LjcgMTAuMi0xLjggMTkuOC0uOCAyOC41IDUuMSA3LjkgNS40IDEyLjggMTIuNyAxNC4xIDIyLjMgMS43IDEzLjUtMi4yIDI0LjUtMTEuNSAzMy45LTYuNiA2LjctMTQuNyAxMC45LTI0IDEyLjgtMi43LjUtNS40LjYtOCAuOXptMjMuOC00MC40Yy0uMS0xLjMtLjEtMi4zLS4zLTMuMy0xLjgtOS45LTEwLjktMTUuNS0yMC40LTEzLjMtOS4zIDIuMS0xNS4zIDgtMTcuNSAxNy40LTEuOCA3LjggMiAxNS43IDkuMiAxOC45IDUuNSAyLjQgMTEgMi4xIDE2LjMtLjYgNy45LTQuMSAxMi4yLTEwLjUgMTIuNy0xOS4xeiIvPjwvc3ZnPg== 34 | [codecov-badge]: https://img.shields.io/codecov/c/github/go-darwin/apfs/master.svg?style=for-the-badge&label=CODECOV&logo=codecov 35 | [release-badge]: https://img.shields.io/github/release/go-darwin/apfs.svg?style=for-the-badge&logo=github&cacheSeconds=60 36 | [ga-badge]: https://gh-ga-beacon.appspot.com/UA-89201129-1/go-darwin/apfs?useReferer&pixel 37 | -------------------------------------------------------------------------------- /copy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | ) 14 | 15 | func TestCopyFile(t *testing.T) { 16 | type args struct { 17 | src string 18 | dst string 19 | state COPYFILE_STATE 20 | flag COPYFILE_FLAG 21 | } 22 | tests := []struct { 23 | name string 24 | args args 25 | want bool 26 | wantErr bool 27 | }{ 28 | { 29 | name: "both file in apfs", 30 | args: args{ 31 | src: testFile, 32 | dst: filepath.Join(mountPoint, "copyfile.txt"), 33 | state: CopyFileStateAlloc(), 34 | flag: COPYFILE_CLONE, 35 | }, 36 | want: true, 37 | wantErr: false, 38 | }, 39 | { 40 | name: "src file not in apfs", 41 | args: args{ 42 | src: testFileGolden, 43 | dst: filepath.Join(mountPoint, "copyfile2.txt"), 44 | state: CopyFileStateAlloc(), 45 | flag: COPYFILE_CLONE, 46 | }, 47 | want: false, 48 | wantErr: false, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | defer CopyFileStateFree(tt.args.state) 54 | 55 | got, err := CopyFile(tt.args.src, tt.args.dst, tt.args.state, tt.args.flag) 56 | if (err != nil) != tt.wantErr { 57 | t.Errorf("CopyFile(%v, %v, %v, %v) error = %v, wantErr %v", tt.args.src, tt.args.dst, tt.args.state, tt.args.flag, err, tt.wantErr) 58 | return 59 | } 60 | if got != tt.want { 61 | t.Errorf("CopyFile(%v, %v, %v, %v) = %v, want %v", tt.args.src, tt.args.dst, tt.args.state, tt.args.flag, got, tt.want) 62 | } 63 | }) 64 | } 65 | } 66 | 67 | func TestFcopyFile(t *testing.T) { 68 | type args struct { 69 | src uintptr 70 | dst uintptr 71 | state COPYFILE_STATE 72 | flag COPYFILE_FLAG 73 | } 74 | tests := []struct { 75 | name string 76 | args args 77 | srcFile string 78 | dstFile string 79 | wantErr bool 80 | }{ 81 | { 82 | name: "both file in apfs", 83 | args: args{ 84 | state: CopyFileStateAlloc(), 85 | flag: COPYFILE_DATA, 86 | }, 87 | srcFile: testFile, 88 | dstFile: filepath.Join(mountPoint, "fcopyfile.txt"), 89 | wantErr: false, 90 | }, 91 | { 92 | name: "src file not in apfs", 93 | args: args{ 94 | state: CopyFileStateAlloc(), 95 | flag: COPYFILE_DATA, 96 | }, 97 | srcFile: testFileGolden, 98 | dstFile: filepath.Join(mountPoint, "fcopyfile2.txt"), 99 | wantErr: false, 100 | }, 101 | } 102 | for _, tt := range tests { 103 | t.Run(tt.name, func(t *testing.T) { 104 | defer CopyFileStateFree(tt.args.state) 105 | 106 | srcFd, err := os.Open(tt.srcFile) 107 | if err != nil { 108 | t.Fatal(err) 109 | } 110 | defer srcFd.Close() 111 | tt.args.src = srcFd.Fd() 112 | 113 | dstFd, err := os.Create(tt.dstFile) 114 | if err != nil { 115 | t.Fatal(err) 116 | } 117 | defer dstFd.Close() 118 | tt.args.dst = dstFd.Fd() 119 | 120 | if err := FcopyFile(tt.args.src, tt.args.dst, tt.args.state, tt.args.flag); (err != nil) != tt.wantErr { 121 | t.Errorf("FcopyFile(%v, %v, %v, %v) error = %v, wantErr %v", tt.args.src, tt.args.dst, tt.args.state, tt.args.flag, err, tt.wantErr) 122 | } 123 | }) 124 | } 125 | } 126 | 127 | // TODO(zchee): implements test 128 | func TestCopyFileStateAlloc(t *testing.T) {} 129 | 130 | func TestCopyFileStateFree(t *testing.T) { 131 | type args struct { 132 | state COPYFILE_STATE 133 | } 134 | tests := []struct { 135 | name string 136 | args args 137 | wantErr bool 138 | }{ 139 | { 140 | name: "simple", 141 | args: args{state: CopyFileStateAlloc()}, 142 | wantErr: false, 143 | }, 144 | } 145 | for _, tt := range tests { 146 | t.Run(tt.name, func(t *testing.T) { 147 | if err := CopyFileStateFree(tt.args.state); (err != nil) != tt.wantErr { 148 | t.Errorf("CopyFileStateFree(%v) error = %v, wantErr %v", tt.args.state, err, tt.wantErr) 149 | } 150 | }) 151 | } 152 | } 153 | 154 | // TODO(zchee): implements test 155 | func TestCopyFileStateGet(t *testing.T) {} 156 | -------------------------------------------------------------------------------- /clone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | /* 10 | #include // for AT_FDCWD 11 | #include // for uint32_t 12 | #include 13 | #include 14 | */ 15 | import "C" 16 | import ( 17 | "fmt" 18 | "path/filepath" 19 | "syscall" 20 | ) 21 | 22 | // CLONEFILE_FLAG provides the clonefile(2) flag. 23 | type CLONEFILE_FLAG uint32 24 | 25 | var ( 26 | // CLONE_NOFOLLOW don't follow the src file if it is a symbolic link (applicable only if the source is not a directory). 27 | // 28 | // The symbolic link is itself cloned if src names a symbolic link. 29 | CLONE_NOFOLLOW CLONEFILE_FLAG = 0x0001 30 | ) 31 | 32 | // CloneFile function causes the named file src to be cloned to the named file dst. 33 | // 34 | // The cloned file dst shares its data blocks with the src file but has its own copy of attributes, 35 | // extended attributes and ACL's which are identical to those of the named file src with the exceptions listed below 36 | // 37 | // 1. ownership information is set as it would be if dst was created by openat(2) or mkdirat(2) or symlinkat(2) if the current 38 | // user does not have privileges to change ownership. 39 | // 40 | // 2. setuid and setgid bits are turned off in the mode bits for regular files. 41 | // 42 | // Subsequent writes to either the original or cloned file are private to the file being modified (copy-on-write). 43 | // 44 | // The named file dst must not exist for the call to be successful. Since the clonefile() system call might not allocate new storage for 45 | // data blocks, it is possible for a subsequent overwrite of an existing data block to return ENOSPC. 46 | // 47 | // If src names a directory, the directory hierarchy is cloned as if each item was cloned individually. 48 | // 49 | // However, the use of copyfile(3) is more appropriate for copying large directory hierarchies instead of clonefile(2). 50 | // 51 | // int 52 | // clonefile(const char * src, const char * dst, int flags); 53 | func CloneFile(src, dst string, flag CLONEFILE_FLAG) error { 54 | if err := C.clonefile(C.CString(src), C.CString(dst), C.uint32_t(flag)); err != 0 { 55 | return fmt.Errorf("error: C.clonefile: %v", syscall.Errno(err)) 56 | } 57 | 58 | return nil 59 | } 60 | 61 | // CloneFileAt is equivalent to clonefile() except in the case where either src or dst specifies a relative path. 62 | // 63 | // If src is a relative path, the file to be cloned is located relative to the directory associated with the file descriptor 64 | // src_dirfd instead of the current working directory. 65 | // 66 | // If dst is a relative path, the same happens only relative to the directory associated with dst_dirfd. 67 | // 68 | // If clonefileat() is passed the special value AT_FDCWD in either the src_dirfd or dst_dirfd parameters, 69 | // the current working directory is used in the determination of the file for the respective path parameter. 70 | // 71 | // int 72 | // clonefileat(int src_dirfd, const char * src, int dst_dirfd, const char * dst, int flags); 73 | func CloneFileAt(src, dst string, flag CLONEFILE_FLAG) error { 74 | var srcDirFd C.int 75 | if !filepath.IsAbs(src) { 76 | srcDirFd = C.AT_FDCWD 77 | } 78 | var dstDirFd C.int 79 | if !filepath.IsAbs(dst) { 80 | dstDirFd = C.AT_FDCWD 81 | } 82 | if err := C.clonefileat(srcDirFd, C.CString(src), dstDirFd, C.CString(dst), C.uint32_t(flag)); err != 0 { 83 | return fmt.Errorf("error: C.clonefileat: %v", syscall.Errno(err)) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | // FcloneFileAt function is similar to clonefileat() except that the source is identified by file descriptor srcfd rather 90 | // than a path (as in clonefile() or clonefileat()) 91 | // 92 | // The flags parameter specifies the options that can be passed. 93 | // 94 | // int 95 | // fclonefileat(int srcfd, int dst_dirfd, const char * dst, int flags); 96 | func FcloneFileAt(srcFd uintptr, dst string, flag CLONEFILE_FLAG) error { 97 | var dstDirFd C.int 98 | if !filepath.IsAbs(dst) { 99 | dstDirFd = C.AT_FDCWD 100 | } 101 | if err := C.fclonefileat(C.int(srcFd), dstDirFd, C.CString(dst), C.uint32_t(flag)); err != 0 { 102 | return fmt.Errorf("error: C.fclonefileat: %v", syscall.Errno(err)) 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /hack/make/go.mk: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # global 3 | 4 | SHELL = /usr/bin/env bash 5 | 6 | ifneq ($(shell command -v go),) 7 | GO_PATH ?= $(shell go env GOPATH) 8 | GO_OS ?= $(shell go env GOOS) 9 | GO_ARCH ?= $(shell go env GOARCH) 10 | 11 | PKG := $(subst $(GO_PATH)/src/,,$(CURDIR)) 12 | GO_PKGS := $(shell go list ./... | grep -v -e '.pb.go') 13 | GO_APP_PKGS := $(shell go list -f '{{if and (or .GoFiles .CgoFiles) (ne .Name "main")}}{{.ImportPath}}{{end}}' ${PKG}/...) 14 | GO_TEST_PKGS := $(shell go list -f='{{if or .TestGoFiles .XTestGoFiles}}{{.ImportPath}}{{end}}' ./...) 15 | GO_VENDOR_PKGS= 16 | ifneq ($(wildcard ./vendor),) 17 | GO_VENDOR_PKGS = $(shell go list -f '{{if and (or .GoFiles .CgoFiles) (ne .Name "main")}}./vendor/{{.ImportPath}}{{end}}' ./vendor/...) 18 | endif 19 | 20 | GO_TEST ?= go test 21 | ifneq ($(shell command -v gotest),) 22 | GO_TEST=gotest 23 | endif 24 | GO_TEST_FUNC ?= . 25 | GO_TEST_FLAGS ?= 26 | GO_BENCH_FUNC ?= . 27 | GO_BENCH_FLAGS ?= -benchmem 28 | 29 | CGO_ENABLED ?= 0 30 | GO_GCFLAGS= 31 | GO_LDFLAGS=-s -w 32 | 33 | GO_BUILDTAGS=osusergo 34 | ifneq ($(GO_OS),darwin) 35 | GO_BUILDTAGS+=netgo 36 | endif 37 | GO_BUILDTAGS_STATIC=static static_build 38 | GO_INSTALLSUFFIX_STATIC=netgo 39 | GO_FLAGS ?= -tags='$(GO_BUILDTAGS)' -gcflags="${GO_GCFLAGS}" -ldflags="${GO_LDFLAGS}" 40 | 41 | GO_MOD_FLAGS = 42 | ifneq ($(wildcard go.mod),) # exist go.mod 43 | ifneq ($(GO111MODULE),off) 44 | GO_MOD_FLAGS=-mod=vendor 45 | endif 46 | endif 47 | endif 48 | 49 | # ---------------------------------------------------------------------------- 50 | # defines 51 | 52 | GOPHER = "" 53 | define target 54 | @printf "$(GOPHER) \\x1b[1;32m$(patsubst ,$@,$(1))\\x1b[0m\\n" 55 | endef 56 | 57 | # ---------------------------------------------------------------------------- 58 | # targets 59 | 60 | ## build and install 61 | 62 | .PHONY: pkg/install 63 | pkg/install: GO_FLAGS+=${GO_MOD_FLAGS} 64 | pkg/install: GO_LDFLAGS= 65 | pkg/install: GO_BUILDTAGS= 66 | pkg/install: 67 | $(call target) 68 | GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GO_OS) GOARCH=$(GO_ARCH) go install -v ${GO_APP_PKGS} 69 | 70 | ## test, bench and coverage 71 | 72 | .PHONY: test 73 | test: GO_FLAGS+=${GO_MOD_FLAGS} 74 | test: GO_BUILDTAGS+=${GO_BUILDTAGS_STATIC} 75 | test: GO_FLAGS+=-installsuffix ${GO_INSTALLSUFFIX_STATIC} 76 | test: ## Runs package test including race condition. 77 | $(call target) 78 | @GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) $(GO_TEST) -v -race $(strip $(GO_FLAGS)) -run=$(GO_TEST_FUNC) $(GO_TEST_PKGS) 79 | 80 | .PHONY: bench 81 | bench: GO_FLAGS+=${GO_MOD_FLAGS} 82 | bench: GO_BUILDTAGS+=${GO_BUILDTAGS_STATIC} 83 | bench: GO_FLAGS+=-installsuffix ${GO_INSTALLSUFFIX_STATIC} 84 | bench: ## Take a package benchmark. 85 | $(call target) 86 | @GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) $(GO_TEST) -v $(strip $(GO_FLAGS)) -run='^$$' -bench=$(GO_BENCH_FUNC) -benchmem $(GO_TEST_PKGS) 87 | 88 | .PHONY: bench/race 89 | bench/race: ## Takes packages benchmarks with the race condition. 90 | $(call target) 91 | @GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) $(GO_TEST) -v -race $(strip $(GO_FLAGS)) -run='^$$' -bench=$(GO_BENCH_FUNC) -benchmem $(GO_TEST_PKGS) 92 | 93 | .PHONY: bench/trace 94 | bench/trace: ## Take a package benchmark with take a trace profiling. 95 | $(GO_TEST) -v -c -o bench-trace.test $(PKG) 96 | GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) GODEBUG=allocfreetrace=1 ./bench-trace.test -test.run=none -test.bench=$(GO_BENCH_FUNC) -test.benchmem -test.benchtime=10ms 2> trace.log 97 | 98 | .PHONY: coverage 99 | coverage: GO_FLAGS+=${GO_MOD_FLAGS} 100 | coverage: GO_BUILDTAGS+=${GO_BUILDTAGS_STATIC} 101 | coverage: GO_FLAGS+=-installsuffix ${GO_INSTALLSUFFIX_STATIC} 102 | coverage: ## Takes packages test coverage. 103 | $(call target) 104 | GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) $(GO_TEST) -v -race $(strip $(GO_FLAGS)) -covermode=atomic -coverpkg=$(PKG)/... -coverprofile=coverage.out $(GO_PKGS) 105 | 106 | $(GO_PATH)/bin/go-junit-report: 107 | @GO111MODULE=off go get -u github.com/jstemmer/go-junit-report 108 | 109 | .PHONY: cmd/go-junit-report 110 | cmd/go-junit-report: $(GO_PATH)/bin/go-junit-report # go get 'go-junit-report' binary 111 | 112 | .PHONY: coverage/ci 113 | coverage/ci: GO_FLAGS+=${GO_MOD_FLAGS} 114 | coverage/ci: GO_BUILDTAGS+=${GO_BUILDTAGS_STATIC} 115 | coverage/ci: GO_FLAGS+=-installsuffix ${GO_INSTALLSUFFIX_STATIC} 116 | coverage/ci: cmd/go-junit-report 117 | coverage/ci: ## Takes packages test coverage, and output coverage results to CI artifacts. 118 | $(call target) 119 | @mkdir -p /tmp/ci/artifacts /tmp/ci/test-results 120 | GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) $(GO_TEST) -a -v -race $(strip $(GO_FLAGS)) -covermode=atomic -coverpkg=$(PKG)/... -coverprofile=/tmp/ci/artifacts/coverage.out $(GO_PKGS) 2>&1 | tee /dev/stderr | go-junit-report -set-exit-code > /tmp/ci/test-results/junit.xml 121 | @if [[ -f '/tmp/ci/artifacts/coverage.out' ]]; then go tool cover -html=/tmp/ci/artifacts/coverage.out -o /tmp/ci/artifacts/coverage.html; fi 122 | 123 | 124 | ## lint 125 | 126 | .PHONY: lint 127 | lint: lint/vet lint/golangci-lint ## Run all linters. 128 | 129 | $(GO_PATH)/bin/vet: 130 | @GO111MODULE=off go get -u golang.org/x/tools/go/analysis/cmd/vet golang.org/x/tools/go/analysis/passes/... 131 | 132 | .PHONY: cmd/vet 133 | cmd/vet: $(GO_PATH)/bin/vet # go get 'vet' binary 134 | 135 | .PHONY: lint/vet 136 | lint/vet: cmd/vet 137 | $(call target) 138 | @GO111MODULE=on vet -asmdecl -assign -atomic -atomicalign -bool -bools -buildtag -buildtags -cgocall -compositewhitelist -copylocks -errorsas -httpresponse -loopclosure -lostcancel -methods -nilfunc -printfuncs -rangeloops -shift -source -stdmethods -structtag -tags -tests -unmarshal -unreachable -unsafeptr -unusedfuncs -unusedstringmethods $(GO_PKGS) 139 | 140 | $(GO_PATH)/bin/golangci-lint: 141 | @GO111MODULE=off go get -u github.com/golangci/golangci-lint/cmd/golangci-lint 142 | 143 | .PHONY: cmd/golangci-lint 144 | cmd/golangci-lint: $(GO_PATH)/bin/golangci-lint # go get 'golangci-lint' binary 145 | 146 | .PHONY: lint/golangci-lint 147 | lint/golangci-lint: cmd/golangci-lint .golangci.yml ## Run golangci-lint. 148 | $(call target) 149 | @GO111MODULE=on golangci-lint run ./... 150 | 151 | 152 | ## mod 153 | 154 | .PHONY: mod/init 155 | mod/init: ## Initializes and writes a new `go.mod` to the current directory. 156 | $(call target) 157 | @GO111MODULE=on go mod init > /dev/null 2>&1 || true 158 | 159 | .PHONY: mod/get 160 | mod/get: ## Updates all module packages and go.mod. 161 | $(call target) 162 | @GO111MODULE=on go get -u -m -v -x 163 | 164 | .PHONY: mod/tidy 165 | mod/tidy: ## Makes sure go.mod matches the source code in the module. 166 | $(call target) 167 | @GO111MODULE=on go mod tidy -v 168 | 169 | .PHONY: mod/vendor 170 | mod/vendor: mod/tidy ## Resets the module's vendor directory and fetch all modules packages. 171 | $(call target) 172 | @GO111MODULE=on go mod vendor -v 173 | 174 | .PHONY: mod/graph 175 | mod/graph: ## Prints the module requirement graph with replacements applied. 176 | $(call target) 177 | @GO111MODULE=on go mod graph 178 | 179 | .PHONY: mod/clean 180 | mod/clean: ## Cleanups go.sum and vendor/modules.txt files. 181 | $(call target) 182 | @find vendor -type f \( -name '*_test.go' -o -name '.gitignore' -o -name '*appveyor.yml' -o -name '.travis.yml' -o -name 'circle.yml' -o -name '*.json' -o -name '*.flake8' -o -name 'generate-flag-types' -o -name 'runtests' \) -print -exec rm -f {} ";" 183 | @find vendor -type d \( -name 'testdata' -o -name 'examples' -o -name '.gx' -o -name 'autocomplete' -o -name '.circleci' \) -print | xargs rm -rf 184 | 185 | .PHONY: mod/install 186 | mod/install: mod/tidy mod/vendor 187 | mod/install: ## Install the module vendor package as an object file. 188 | $(call target) 189 | @GO111MODULE=off go install -v $(strip $(GO_FLAGS)) $(GO_VENDOR_PKGS) || GO111MODULE=on go install -mod=vendor -v $(strip $(GO_FLAGS)) $(GO_VENDOR_PKGS) 190 | 191 | .PHONY: mod/update 192 | mod/update: mod/get mod/tidy mod/vendor mod/install ## Updates all of vendor packages. 193 | @GO111MODULE=on go mod edit -go 1.12 194 | 195 | .PHONY: mod 196 | mod: mod/init mod/tidy mod/vendor mod/install 197 | mod: ## Updates the vendoring directory using go mod. 198 | @GO111MODULE=on go mod edit -go 1.12 199 | 200 | 201 | ## clean 202 | 203 | .PHONY: clean 204 | clean: ## Cleanups binaries and extra files in the package. 205 | $(call target) 206 | @$(RM) $(APP) *.out *.test *.prof trace.log 207 | 208 | 209 | ## boilerplate 210 | 211 | .PHONY: boilerplate/go/% 212 | boilerplate/go/%: BOILERPLATE_PKG_DIR=$(shell printf $@ | cut -d'/' -f3- | rev | cut -d'/' -f2- | rev) 213 | boilerplate/go/%: BOILERPLATE_PKG_NAME=$(if $(findstring main,$@),main,$(shell printf $@ | rev | cut -d/ -f2 | rev)) 214 | boilerplate/go/%: hack/boilerplate/boilerplate.go.txt 215 | boilerplate/go/%: ## Creates a go file based on boilerplate.go.txt in % location. 216 | @if [[ ! ${BOILERPLATE_PKG_DIR} == *'.go'* ]] && [ ! -d ${BOILERPLATE_PKG_DIR} ]; then mkdir -p ${BOILERPLATE_PKG_DIR}; fi 217 | @cat hack/boilerplate/boilerplate.go.txt <(printf "\npackage ${BOILERPLATE_PKG_NAME}\\n") > $* 218 | @sed -i "s|YEAR|$(shell date '+%Y')|g" $* 219 | 220 | 221 | ## miscellaneous 222 | 223 | .PHONY: AUTHORS 224 | AUTHORS: ## Creates AUTHORS file. 225 | @$(file >$@,# This file lists all individuals having contributed content to the repository.) 226 | @$(file >>$@,# For how it is generated, see `make AUTHORS`.) 227 | @printf "$(shell git log --format="\n%aN <%aE>" | LC_ALL=C.UTF-8 sort -uf)" >> $@ 228 | 229 | .PHONY: todo 230 | TODO: ## Print the all of (TODO|BUG|XXX|FIXME|NOTE) in packages. 231 | @rg -e '(TODO|BUG|XXX|FIXME|NOTE)(\(.+\):|:)' --follow --hidden --glob='!.git' --glob='!vendor' --glob='!internal' --glob='!Makefile' --glob='!snippets' --glob='!indent' 232 | 233 | .PHONY: help 234 | help: ## Show make target help. 235 | @perl -nle 'BEGIN {printf "Usage:\n make \033[33m\033[0m\n\nTargets:\n"} printf " \033[36m%-30s\033[0m %s\n", $$1, $$2 if /^([a-zA-Z\/_-].+)+:.*?\s+## (.*)/' ${MAKEFILE_LIST} 236 | -------------------------------------------------------------------------------- /copy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-darwin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package apfs 8 | 9 | /* 10 | #include 11 | #include 12 | */ 13 | import "C" 14 | import ( 15 | "fmt" 16 | "syscall" 17 | "unsafe" 18 | ) 19 | 20 | // COPYFILE_FLAG provides the copyfile flag. 21 | type COPYFILE_FLAG int 22 | 23 | var ( 24 | // COPYFILE_ACL copy the source file's access control lists. 25 | COPYFILE_ACL COPYFILE_FLAG = 1 << 0 26 | // COPYFILE_STAT copy the source file's POSIX information (mode, modification time, etc.). 27 | COPYFILE_STAT COPYFILE_FLAG = 1 << 1 28 | // COPYFILE_XATTR copy the source file's extended attributes. 29 | COPYFILE_XATTR COPYFILE_FLAG = 1 << 2 30 | // COPYFILE_DATA copy the source file's data. 31 | COPYFILE_DATA COPYFILE_FLAG = 1 << 3 32 | 33 | // COPYFILE_SECURITY copy the source file's POSIX and ACL information; equivalent to (COPYFILE_STAT|COPYFILE_ACL). 34 | COPYFILE_SECURITY = COPYFILE_STAT | COPYFILE_ACL 35 | // COPYFILE_METADATA copy the metadata; equivalent to (COPYFILE_SECURITY|COPYFILE_XATTR). 36 | COPYFILE_METADATA = COPYFILE_SECURITY | COPYFILE_XATTR 37 | // COPYFILE_ALL copy the entire file; equivalent to (COPYFILE_METADATA|COPYFILE_DATA). 38 | COPYFILE_ALL = COPYFILE_METADATA | COPYFILE_DATA 39 | 40 | // COPYFILE_RECURSIVE causes CopyFile to recursively copy a hierarchy. 41 | // 42 | // This flag is not used by FcopyFile; see below for more information. 43 | COPYFILE_RECURSIVE COPYFILE_FLAG = 1 << 15 // Descend into hierarchies 44 | // COPYFILE_CHECK return a bitmask (corresponding to the flags argument) indicating which contents would be copied 45 | // no data are actually copied. 46 | // 47 | // (E.g., if flags was set to COPYFILE_CHECK|COPYFILE_METADATA, and the from 48 | // file had extended attributes but no ACLs, the return value would be COPYFILE_XATTR .) 49 | COPYFILE_CHECK COPYFILE_FLAG = 1 << 16 // return flags for xattr or acls if set 50 | // COPYFILE_EXCL fail if the to file already exists. (This is only applicable for the CopyFile function.) 51 | COPYFILE_EXCL COPYFILE_FLAG = 1 << 17 // fail if destination exists 52 | // COPYFILE_NOFOLLOW_SRC do not follow the from file, if it is a symbolic link. (This is only applicable for the CopyFile function.) 53 | COPYFILE_NOFOLLOW_SRC COPYFILE_FLAG = 1 << 18 // don't follow if source is a symlink 54 | // COPYFILE_NOFOLLOW_DST do not follow the to file, if it is a symbolic link. (This is only applicable for the CopyFile function.) 55 | COPYFILE_NOFOLLOW_DST COPYFILE_FLAG = 1 << 19 // don't follow if dst is a symlink 56 | // COPYFILE_MOVE unlink (using remove(3)) the from file. (This is only applicable for the copyfile() function.) No error is returned if remove(3) fails. 57 | // 58 | // Note that remove(3) removes a symbolic link itself, not the target of the link. 59 | COPYFILE_MOVE COPYFILE_FLAG = 1 << 20 // unlink src after copy 60 | // COPYFILE_UNLINK unlink the to file before starting. (This is only applicable for the copyfile() function.) 61 | COPYFILE_UNLINK COPYFILE_FLAG = 1 << 21 // unlink dst before copy 62 | // COPYFILE_NOFOLLOW this is a convenience macro, equivalent to (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC). 63 | COPYFILE_NOFOLLOW = COPYFILE_NOFOLLOW_SRC | COPYFILE_NOFOLLOW_DST 64 | 65 | // COPYFILE_PACK serialize the from file. The to file is an AppleDouble-format file. 66 | COPYFILE_PACK COPYFILE_FLAG = 1 << 22 67 | // COPYFILE_UNPACK unserialize the from file. 68 | // 69 | // The from file is an AppleDouble-format file; the to file will have the extended attributes, ACLs, resource fork, and FinderInfo data from the to file, regardless of the flags argument passed in. 70 | COPYFILE_UNPACK COPYFILE_FLAG = 1 << 23 71 | 72 | // COPYFILE_CLONE try to clone the file/directory instead. 73 | // 74 | // This is a best try flag i.e. if cloning fails, fallback to copying the file. 75 | // 76 | // This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA). 77 | // 78 | // Note that if cloning is successful, callbacks will not be invoked. 79 | COPYFILE_CLONE COPYFILE_FLAG = 1 << 24 80 | // COPYFILE_CLONE_FORCE clone the file/directory instead. This is a force flag i.e. if cloning fails, an error is returned. 81 | // 82 | // This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA). 83 | // 84 | // Note that if cloning is successful, callbacks will not be invoked. 85 | COPYFILE_CLONE_FORCE COPYFILE_FLAG = 1 << 25 86 | 87 | // COPYFILE_RUN_IN_PLACE if the src file has quarantine information, add the QTN_FLAG_DO_NOT_TRANSLOCATE flag to the quarantine information of the dst file. 88 | // 89 | // This allows a bundle to run in place instead of being translocated. 90 | COPYFILE_RUN_IN_PLACE COPYFILE_FLAG = 1 << 26 91 | 92 | COPYFILE_VERBOSE COPYFILE_FLAG = 1 << 30 93 | ) 94 | 95 | // COPYFILE_STATE_FLAG provides the copyfile state flag. 96 | type COPYFILE_STATE_FLAG uint32 97 | 98 | var ( 99 | // COPYFILE_STATE_SRC_FD get or set the file descriptor associated with the source (or destination) file. 100 | // 101 | // If this has not been initialized yet, the value will be -2. 102 | // 103 | // The dst (for CopyFileStateGet) and src (for CopyFileStateSet) parameters are pointers to int. 104 | COPYFILE_STATE_SRC_FD COPYFILE_STATE_FLAG = C.COPYFILE_STATE_SRC_FD 105 | // COPYFILE_STATE_DST_FD get or set the file descriptor associated with the source (or destination) file. 106 | // 107 | // If this has not been initialized yet, the value will be -2. 108 | // 109 | // The dst (for CopyFileStateGet) and src (for CopyFileStateSet) parameters are pointers to int. 110 | COPYFILE_STATE_DST_FD COPYFILE_STATE_FLAG = C.COPYFILE_STATE_DST_FD 111 | // COPYFILE_STATE_SRC_FILENAME get or set the filename associated with the source (or destination) file. 112 | // 113 | // If it has not been initialized yet, the value will be NULL. 114 | // 115 | // For copyfile_state_set(), the src parameter is a pointer to a C string (i.e., char* ); copyfile_state_set() makes a private copy of this string. 116 | // 117 | // For copyfile_state_get() function, the dst parameter is a pointer to a pointer to a C string (i.e., char** ); the returned value is a pointer to the state 's copy, and must not be modified or released. 118 | COPYFILE_STATE_SRC_FILENAME COPYFILE_STATE_FLAG = C.COPYFILE_STATE_SRC_FILENAME 119 | // COPYFILE_STATE_DST_FILENAME get or set the filename associated with the source (or destination) file. 120 | // 121 | // If it has not been initialized yet, the value will be NULL. 122 | // 123 | // For copyfile_state_set(), the src parameter is a pointer to a C string (i.e., char* ); copyfile_state_set() makes a private copy of this string. 124 | // 125 | // For copyfile_state_get() function, the dst parameter is a pointer to a pointer to a C string (i.e., char** ); the returned value is a pointer to the state 's copy, and must not be modified or released. 126 | COPYFILE_STATE_DST_FILENAME COPYFILE_STATE_FLAG = C.COPYFILE_STATE_DST_FILENAME 127 | // COPYFILE_STATE_STATUS_CB get or set the callback status function (currently only used for recursive copies; see below for details). 128 | // 129 | // The src parameter is a pointer to a function of type copyfile_callback_t (see above). 130 | COPYFILE_STATE_STATUS_CB COPYFILE_STATE_FLAG = C.COPYFILE_STATE_STATUS_CB 131 | // COPYFILE_STATE_STATUS_CTX get or set the context parameter for the status call-back function (see below for details). 132 | // 133 | // The src parameter is a void *. 134 | COPYFILE_STATE_STATUS_CTX COPYFILE_STATE_FLAG = C.COPYFILE_STATE_STATUS_CTX 135 | // COPYFILE_STATE_QUARANTINE get or set the quarantine information with the source file. 136 | // 137 | // The src parameter is a pointer to an opaque object (type void * ). 138 | COPYFILE_STATE_QUARANTINE COPYFILE_STATE_FLAG = C.COPYFILE_STATE_QUARANTINE 139 | // COPYFILE_STATE_COPIED get the number of data bytes copied so far. (Only valid for copyfile_state_get(); see below for 140 | // more details about callbacks.) 141 | // 142 | // If a COPYFILE_CLONE or COPYFILE_CLONE_FORCE operation successfully cloned the requested objects, then this value will be 0. 143 | // 144 | // The dst parameter is a pointer to off_t (type off_t * ). 145 | COPYFILE_STATE_COPIED COPYFILE_STATE_FLAG = C.COPYFILE_STATE_COPIED 146 | // COPYFILE_STATE_XATTRNAME get the name of the extended attribute during a callback for COPYFILE_COPY_XATTR (see below for details). 147 | // 148 | // This field cannot be set, and may be NULL. 149 | COPYFILE_STATE_XATTRNAME COPYFILE_STATE_FLAG = C.COPYFILE_STATE_XATTRNAME 150 | // COPYFILE_STATE_WAS_CLONED true if a COPYFILE_CLONE or COPYFILE_CLONE_FORCE operation successfully cloned the requested objects. 151 | // 152 | // The dst parameter is a pointer to bool (type bool * ). 153 | COPYFILE_STATE_WAS_CLONED COPYFILE_STATE_FLAG = C.COPYFILE_STATE_WAS_CLONED 154 | ) 155 | 156 | // COPYFILE_RECURSE_CALLBACK provides the copyfile callbacks. 157 | type COPYFILE_RECURSE_CALLBACK uint32 158 | 159 | var ( 160 | // COPYFILE_RECURSE_ERROR there was an error in processing an element of the source hierarchy. 161 | // 162 | // This happens when fts(3) returns an error or unknown file type. (Currently, the second argument to the call-back function will always be COPYFILE_ERR in this case.) 163 | COPYFILE_RECURSE_ERROR COPYFILE_RECURSE_CALLBACK = C.COPYFILE_RECURSE_ERROR // 0 164 | // COPYFILE_RECURSE_FILE the object being copied is a file (or, rather, something other than a directory). 165 | COPYFILE_RECURSE_FILE COPYFILE_RECURSE_CALLBACK = C.COPYFILE_RECURSE_FILE // 1 166 | // COPYFILE_RECURSE_DIR the object being copied is a directory, and is being entered. (That is, none of the filesystem objects contained within the directory have been copied yet.) 167 | COPYFILE_RECURSE_DIR COPYFILE_RECURSE_CALLBACK = C.COPYFILE_RECURSE_DIR // 2 168 | // COPYFILE_RECURSE_DIR_CLEANUP the object being copied is a directory, and all of the objects contained have been copied. 169 | // 170 | // At this stage, the destination directory being copied will have any extra permissions that were added to allow the copying will be removed. 171 | COPYFILE_RECURSE_DIR_CLEANUP COPYFILE_RECURSE_CALLBACK = C.COPYFILE_RECURSE_DIR_CLEANUP // 3 172 | COPYFILE_COPY_DATA COPYFILE_RECURSE_CALLBACK = C.COPYFILE_COPY_DATA // 4 173 | COPYFILE_COPY_XATTR COPYFILE_RECURSE_CALLBACK = C.COPYFILE_COPY_XATTR // 5 174 | ) 175 | 176 | var ( 177 | // COPYFILE_START before copying has begun. The third parameter will be a newly-created copyfile_state_t object with the callback function and context pre-loaded. 178 | COPYFILE_START COPYFILE_RECURSE_CALLBACK = C.COPYFILE_START // 1 179 | // COPYFILE_FINISH after copying has successfully finished. 180 | COPYFILE_FINISH COPYFILE_RECURSE_CALLBACK = C.COPYFILE_FINISH // 2 181 | // COPYFILE_ERR indicates an error has happened at some stage. 182 | // 183 | // If the first argument to the call-back function is COPYFILE_RECURSE_ERROR, then an error occurred while processing the source hierarchy; 184 | // 185 | // otherwise, it will indicate what type of object was being copied, and errno will be set to indicate the error. 186 | COPYFILE_ERR COPYFILE_RECURSE_CALLBACK = C.COPYFILE_ERR // 3 187 | COPYFILE_PROGRESS COPYFILE_RECURSE_CALLBACK = C.COPYFILE_PROGRESS // 4 188 | ) 189 | 190 | var ( 191 | // COPYFILE_CONTINUE the copy will continue as expected. 192 | COPYFILE_CONTINUE COPYFILE_RECURSE_CALLBACK = C.COPYFILE_CONTINUE // 0 193 | // COPYFILE_SKIP this object will be skipped, and the next object will be processed. 194 | // 195 | // (Note that, when entering a directory. returning COPYFILE_SKIP from the call-back function will prevent the contents of the directory from being copied.) 196 | COPYFILE_SKIP COPYFILE_RECURSE_CALLBACK = C.COPYFILE_SKIP // 1 197 | // COPYFILE_QUIT the entire copy is aborted at this stage. Any filesystem objects created up to this point will remain. 198 | // 199 | // CopyFile will return -1, but errno will be unmodified. 200 | COPYFILE_QUIT COPYFILE_RECURSE_CALLBACK = C.COPYFILE_QUIT // 2 201 | ) 202 | 203 | // COPYFILE_STATE provides the copyfile state. 204 | type COPYFILE_STATE C.copyfile_state_t 205 | 206 | // CopyFile function can copy the named from file to the named to file. 207 | // 208 | // If the state parameter is the return value from CopyFileStateAlloc, then CopyFile will use the information from the state object. 209 | // 210 | // If it is NULL, then both functions will work normally, but less control will be available to the caller. 211 | // 212 | // int 213 | // copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags); 214 | func CopyFile(src, dst string, state COPYFILE_STATE, flag COPYFILE_FLAG) (bool, error) { 215 | if err := C.copyfile(C.CString(src), C.CString(dst), state, C.copyfile_flags_t(flag)); err != 0 { 216 | return false, fmt.Errorf("couldn't copy from %s to %s: %v", src, dst, syscall.Errno(err)) 217 | } 218 | 219 | var isCloned int 220 | if err := CopyFileStateGet(state, COPYFILE_STATE_WAS_CLONED, &isCloned); err != nil { 221 | return false, fmt.Errorf("couldn't get copyfile_state_get: %v", err) 222 | } 223 | 224 | return isCloned != 0, nil 225 | } 226 | 227 | // FcopyFile function does the same CopyFile, but using the file descriptors of already-opened files. 228 | // 229 | // If the state parameter is the return value from CopyFileStateAlloc, then FcopyFile will use the information from the state object. 230 | // 231 | // If it is NULL, then both functions will work normally, but less control will be available to the caller. 232 | // 233 | // int 234 | // fcopyfile(int from, int to, copyfile_state_t state, copyfile_flags_t flags); 235 | func FcopyFile(src, dst uintptr, state COPYFILE_STATE, flag COPYFILE_FLAG) error { 236 | if err := C.fcopyfile(C.int(src), C.int(dst), state, C.copyfile_flags_t(flag)); err != 0 { 237 | return fmt.Errorf("couldn't fcopy from %d to %d: %v", src, dst, syscall.Errno(err)) 238 | } 239 | 240 | return nil 241 | } 242 | 243 | // CopyFileStateAlloc function initializes a copyfile_state_t object (which is an opaque data type). 244 | // 245 | // This object can be passed to CopyFile and FcopyFile; CopyfileStateGet and CopyFileStateSet can be used to manipulate the state (see below). 246 | // 247 | // copyfile_state_t 248 | // copyfile_state_alloc(void); 249 | func CopyFileStateAlloc() COPYFILE_STATE { 250 | return COPYFILE_STATE(C.copyfile_state_alloc()) 251 | } 252 | 253 | // CopyFileStateFree function is used to deallocate the object and its contents. 254 | // 255 | // int 256 | // copyfile_state_free(copyfile_state_t state); 257 | func CopyFileStateFree(state COPYFILE_STATE) error { 258 | if err := C.copyfile_state_free(C.copyfile_state_t(state)); err != 0 { 259 | return syscall.Errno(err) 260 | } 261 | 262 | return nil 263 | } 264 | 265 | // CopyFileStateGet functions can be used to manipulate the COPYFILE_STATE object returned by CopyFileStateAlloc. 266 | // 267 | // The dst parameter's type depends on the flag parameter that is passed in. 268 | // 269 | // int 270 | // copyfile_state_get(copyfile_state_t state, uint32_t flag, void * dst); 271 | func CopyFileStateGet(state COPYFILE_STATE, flag COPYFILE_STATE_FLAG, result *int) error { 272 | if err := C.copyfile_state_get(state, C.uint32_t(flag), unsafe.Pointer(result)); err != 0 { 273 | return syscall.Errno(err) 274 | } 275 | 276 | return nil 277 | } 278 | 279 | // CopyFileStateSet functions can be used to manipulate the COPYFILE_STATE object returned by CopyFileStateAlloc. 280 | // 281 | // The dst parameter's type depends on the flag parameter that is passed in. 282 | // 283 | // int 284 | // copyfile_state_set(copyfile_state_t state, uint32_t flag, const void * src); 285 | func CopyFileStateSet(state COPYFILE_STATE, flag COPYFILE_STATE_FLAG, result *int) error { 286 | if err := C.copyfile_state_set(state, C.uint32_t(flag), unsafe.Pointer(result)); err != 0 { 287 | return syscall.Errno(err) 288 | } 289 | 290 | return nil 291 | } 292 | -------------------------------------------------------------------------------- /docs/apfs.md: -------------------------------------------------------------------------------- 1 | # Apple File System 2 | 3 | THIS DOCUMENT COPIED FROM THE [Apple File System Guide](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40016999) 4 | 5 | ## Introduction 6 | 7 | [Introduction](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40016999-CH1-DontLinkElementID_18) 8 | 9 | HFS+ and its predecessor HFS are more than 30 years old. These file systems were developed in an era of floppy disks and spinning hard drives, when file sizes were calculated in kilobytes or megabytes. 10 | 11 | Today, people commonly store hundreds of gigabytes and access millions of files on high-speed, low-latency flash drives. People carry their data with them, and they demand that sensitive information be secure. 12 | 13 | Apple File System is a new, modern file system for iOS, macOS, tvOS, and watchOS. It is optimized for Flash/SSD storage and features strong encryption, copy-on-write metadata, space sharing, cloning for files and directories, snapshots, fast directory sizing, atomic safe-save primitives, and improved file system fundamentals. 14 | 15 | A Developer Preview of Apple File System is available in macOS Sierra. Apple plans to release Apple File System as a bootable file system in 2017. 16 | 17 | ### Prerequisites 18 | 19 | To understand how your app interacts with the file system, read [File System Programming Guide](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010672). 20 | 21 | 22 | ## Features 23 | 24 | - [Fuatures](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_17) 25 | - [Clones](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_4") 26 | - [Snapshots](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_5") 27 | - [Space Sharing](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_6") 28 | - [Encryption](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_7") 29 | - [Crash Protection](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_8) 30 | - [Sparse Files](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_9) 31 | - [Fast Directory Sizing](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_10) 32 | - [Atomic Safe-Save](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/Features/Features.html#//apple_ref/doc/uid/TP40016999-CH5-DontLinkElementID_11) 33 | 34 | Apple File System is a 64-bit file system supporting over 9 quintillion files on a single volume. This state-of-the-art file system features cloning for files and directories, snapshots, space sharing, fast directory sizing, atomic safe-save primitives, and improved filesystem fundamentals, as well as a unique copy-on-write design that uses I/O coalescing to deliver maximum performance while ensuring data reliability. 35 | 36 | ### Clones 37 | 38 | A clone is a nearly instantaneous copy of a file or directory that occupies no additional space for file data. Clones allow the operating system to make fast, power-efficient file copies on the same volume without occupying additional storage space. 39 | 40 | Modifications to the data write the new data elsewhere and continue to share the unmodified blocks. Changes to a file are saved as deltas of the cloned file, reducing storage space required for document revisions and copies. 41 | 42 | ### Snapshots 43 | 44 | A volume snapshot is a point-in-time, read-only instance of the file system. 45 | 46 | The operating system uses snapshots to make backups work more efficiently and offer a way to revert changes to a given point in time. 47 | 48 | ### Space Sharing 49 | 50 | Space Sharing allows multiple file systems to share the same underlying free space on a physical volume. Unlike rigid partitioning schemes that pre-allocate a fixed amount of space for each file system, APFS-formatted volumes can grow and shrink without volume repartitioning. 51 | 52 | Each volume in an APFS container reports the same available disk space, which is equal to the total available disk space of the container. For example, for an APFS container with a capacity of 100GB that contains volume A (which uses 10GB) and volume B (which uses 20GB), the free space reported for both volumes A and B is 70GB (100GB - 10GB - 20GB). 53 | 54 | ### Encryption 55 | 56 | Security and privacy are fundamental in the design of Apple File System. That's why Apple File System implements strong full-disk encryption, encrypting files and all sensitive metadata. 57 | 58 | Which encryption methods are available depends on hardware and operating system support, and can vary for Mac, iPhone, iPad, Apple TV, and Apple Watch. 59 | 60 | Apple File System supports the following encryption models for each volume in a container: 61 | 62 | - No encryption 63 | - Single-key encryption 64 | - Multi-key encryption with per-file keys for file data and a separate key for sensitive metadata 65 | 66 | Multi-key encryption ensures the integrity of user data. Even if someone were to compromise the physical security of the device and gain access to the device key, they still couldn't decrypt the user's files. 67 | 68 | Apple File System uses AES-XTS or AES-CBC encryption modes, depending on hardware. 69 | 70 | ### Crash Protection 71 | 72 | Apple File System uses a novel copy-on-write metadata scheme to ensure that updates to the file system are crash protected, without the write-twice overhead of journaling. 73 | 74 | ### Sparse Files 75 | 76 | Apple File System supports sparse files, a more efficient way of representing empty blocks on disk. With sparse files storage is allocated only when actually needed. 77 | 78 | ### Fast Directory Sizing 79 | 80 | Fast Directory Sizing allows Apple File System to quickly compute the total space used by a directory hierarchy, and update it as the hierarchy evolves. 81 | 82 | ### Atomic Safe-Save 83 | 84 | Apple File System introduces a new Atomic Safe-Save primitive for bundles and directories. Atomic Safe-Save performs renames in a single transaction such that, from the user’s perspective, the operation either is completed or does not happen at all. 85 | 86 | 87 | ## Frequently Asked Questions 88 | 89 | - [Frequently Asked Questions](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_16) 90 | - [Compatibility](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_1) 91 | - [Upgrading](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_2) 92 | - [Implementation](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_3) 93 | 94 | ### Compatibility 95 | 96 | #### What are the limitations of Apple File System in macOS Sierra? 97 | 98 | macOS Sierra includes a Developer Preview release of Apple File System. As a Developer Preview, it has several limitations: 99 | 100 | - Startup Disk: An APFS-formatted volume cannot be used as a startup disk. 101 | - Case Sensitivity: Filenames are case-sensitive only. 102 | - Time Machine: Time Machine backups are not supported. 103 | - FileVault: APFS-formatted volumes cannot be encrypted using FileVault. 104 | - Fusion Drive: Apple File System cannot use Fusion Drives. 105 | 106 | #### Can I use Apple File System with my existing hard disk drive? 107 | 108 | Yes. Apple File System is optimized for Flash/SSD storage, but can also be used with traditional hard disk drives (HDD) and external, direct-attached storage. 109 | 110 | #### Can I reshare APFS-formatted volumes using a network file-sharing protocol? 111 | 112 | Yes, you can share APFS-formatted volumes using the SMB or NFS network file-sharing protocol. 113 | 114 | You cannot share APFS-formatted volumes using AFP. The AFP protocol is deprecated. 115 | 116 | #### Can I use my third-party disk utilities with an APFS-formatted hard disk? 117 | 118 | Existing third-party utilities will need to be updated to support Apple File System. Consult the utility's documentation, or contact the vendor for compatibility information. 119 | 120 | #### Can I boot macOS Sierra from an APFS-formatted hard disk? 121 | 122 | No. macOS Sierra supports Apple File System for data volumes only. You cannot boot macOS Sierra from a APFS-formatted volume. 123 | 124 | ### Upgrading 125 | 126 | #### How do I upgrade to Apple File System? 127 | 128 | Apple will offer nondestructive in-place upgrades from HFS+ to APFS for all boot volumes when Apple File System ships in 2017. Tools will be available to convert external volumes from HFS+ to APFS format. 129 | 130 | #### If I convert a volume to APFS, can I later revert to HFS+? 131 | 132 | You can use Disk Utility to erase an APFS-formatted volume and reformat as HFS+. However, your data will not be preserved when you reformat the volume as HFS+. 133 | 134 | ### Implementation 135 | 136 | #### Why did Apple develop APFS? 137 | 138 | Apple File System is uniquely designed to meet the needs of Apple’s products and ecosystem. Apple File System provides strong encryption, ultra-low latencies and limited memory overhead. It is optimized for Flash/SSD storage and can be used on everything from an Apple Watch to a Mac Pro. 139 | 140 | #### Can RAID be used with Apple File System? 141 | 142 | Yes. Apple File System does not directly implement software RAID; however APFS-formatted volumes can be combined with an Apple RAID volume to support Striping (RAID 0), Mirroring (RAID 1), and Concatenation (JBOD). APFS-formatted volumes can also be used with direct-attached hardware RAID solutions. 143 | 144 | #### Does Apple File System support directory hard links? 145 | 146 | Directory hard links are not supported by Apple File System. All directory hard links are converted to symbolic links or aliases when you convert from HFS+ to APFS volume formats on macOS. 147 | 148 | #### Does Apple File System support ditto blocks? 149 | 150 | Ditto blocks are primarily used to protect against corrupted sectors in large storage arrays with unreliable hard disk drives. Apple File System takes advantage of modern hardware with strong checksums and error correction in firmware, and does not depend on ditto blocks. 151 | 152 | #### Does Apple File System support redundant metadata? 153 | 154 | With modern Flash/SSD storage, writing two blocks of data to different locations does not guarantee that the blocks will be written to separate locations. The Flash translation layer typically groups writes together into the same NAND block. Therefore it affords no extra protection to write a second copy at the same time the first copy is written. 155 | 156 | #### What has Apple done to ensure the reliability of my data? 157 | 158 | Apple products are designed to prevent data corruption and protect against data loss. 159 | 160 | To protect data from hardware errors, all Flash/SSD and hard disk drives used in Apple products use Error Correcting Code (ECC). ECC checks for transmission errors, and when necessary, corrects on the fly. Apple File System uses a unique copy-on-write scheme to protect against data loss that can occur during a crash or loss of power. And to further ensure data integrity, Apple File System uses the Fletcher's checksum algorithm for metadata operations. 161 | 162 | #### Does Apple File System use journaling? 163 | 164 | Apple File System uses copy-on-write to avoid in-place changes to file data, which ensures that file system updates are crash protected without the write-twice overhead of journaling. 165 | 166 | #### Does Apple File System support data deduplication? 167 | 168 | No. With Apple File System individual extents can be encrypted, making it impossible to examine and deduplicate files and their content. Apple File System uses clone files to minimize data storage and data duplication. 169 | 170 | #### Does Apple File System support TRIM operations? 171 | 172 | Yes. TRIM operations are issued asynchronously from when files are deleted or free space is reclaimed, which ensures that these operations are performed only after metadata changes are persisted to stable storage. 173 | 174 | #### Is APFS open source? 175 | 176 | An open source implementation is not available at this time. Apple plans to document and publish the APFS volume format specification when Apple File System is released for macOS in 2017. 177 | 178 | 179 | ## Tools and APIs 180 | 181 | - [Tools and APIs](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/ToolsandAPIs/ToolsandAPIs.html#//apple_ref/doc/uid/TP40016999-CH7-DontLinkElementID_20) 182 | - [Disk Image Tools](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/ToolsandAPIs/ToolsandAPIs.html#//apple_ref/doc/uid/TP40016999-CH7-DontLinkElementID_13) 183 | - [Enhanced APIs](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/ToolsandAPIs/ToolsandAPIs.html#//apple_ref/doc/uid/TP40016999-CH7-DontLinkElementID_14) 184 | 185 | ### Disk Image Tools 186 | 187 | #### hdiutil 188 | 189 | ```sh 190 | hdiutil create -fs APFS -size 1GB foo.sparseimage 191 | ``` 192 | 193 | #### diskutil apfs ... 194 | 195 | ```sh 196 | diskutil apfs createContainer /dev/disk1s1 197 | 198 | diskutil apfs addVolume disk1s1 APFS newAPFS 199 | ``` 200 | 201 | #### fsck_apfs 202 | 203 | ```sh 204 | fsck_apfs 205 | ``` 206 | 207 | ### Enhanced APIs 208 | 209 | #### Foundation / FileManager 210 | 211 | ```c 212 | func copyItem(atPath srcPath: String, 213 | toPath dstPath: String) throws 214 | ``` 215 | 216 | ```c 217 | func replaceItem(at originalItemURL: URL, 218 | withItemAt newItemURL: URL, 219 | backupItemName backupItemName: String?, 220 | options options: 221 | FileManager.ItemReplacementOptions = [], 222 | resultingItemURL resultingURL: 223 | AutoreleasingUnsafeMutablePointer?) throws 224 | ``` 225 | 226 | #### libcopyfile 227 | 228 | ```c 229 | #include 230 | 231 | int copyfile(const char *from, 232 | const char *to, 233 | copyfile_state_t state, 234 | copyfile_flags_t flags); 235 | 236 | int fcopyfile(int from_fd, 237 | int to_fd, 238 | copyfile_state_t state, 239 | copyfile_flags_t flags); 240 | // new flag bit: COPYFILE_CLONE 241 | // equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA) 242 | ``` 243 | 244 | #### Safe Save APIs 245 | 246 | ```c 247 | #include 248 | 249 | int renamex_np(const char *, const char *, unsigned int) 250 | 251 | int renameatx_np(int, const char *, int, const char *, unsigned int) 252 | ``` 253 | 254 | #### Cloning APIs 255 | 256 | ```c 257 | #include 258 | #include 259 | 260 | int clonefileat(int, const char *, int, const char *, uint32_t); 261 | int fclonefileat(int, int, const char *, uint32_t); 262 | int clonefile(const char *, const char *, uint32_t); 263 | ``` 264 | 265 | #### Snapshot APIs 266 | 267 | The Snapshot API is still under development. Contact [Apple Developer Relations](https://developer.apple.com/contact/) for more information. 268 | 269 | 270 | ## Volume Format Comparison 271 | 272 | - [Volume Format Comparison](https://developer.apple.com/library/prerelease/content/documentation/FileManagement/Conceptual/APFS_Guide/VolumeFormatComparison/VolumeFormatComparison.html#//apple_ref/doc/uid/TP40016999-CH8-DontLinkElementID_21) 273 | 274 | | | Mac OS Extended (HFS+) | Apple File System (APFS) | 275 | |-----------------------------|------------------------|--------------------------| 276 | | Number of allocation blocks | 232 (4 billion) | 263 (9 quintillion) | 277 | | File IDs | 32-bit | 64-bit | 278 | | Maximum file size | 263 bytes | 263 bytes | 279 | | Time stamp granularity | 1 second | 1 nanosecond | 280 | | Copy-on-write | | ✔ | 281 | | Crash protected | | ✔ | 282 | | Journaled | | ✔ | 283 | | File and directory clones | | ✔ | 284 | | Snapshots | | ✔ | 285 | | Space sharing | | ✔ | 286 | | Full disk encryption | ✔ | ✔ | 287 | | Sparse files | | ✔ | 288 | | Fast directory sizing | | ✔ | 289 | --------------------------------------------------------------------------------