github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/interrupt_posix.go (about) 1 //go:build !windows 2 3 package filesystem 4 5 import ( 6 "io" 7 8 "golang.org/x/sys/unix" 9 10 "github.com/mutagen-io/mutagen/pkg/filesystem/internal/syscall" 11 ) 12 13 // openatRetryingOnEINTR is a wrapper around the openat system call that retries 14 // on EINTR errors and returns on the first successful call or non-EINTR error. 15 func openatRetryingOnEINTR(directory int, path string, flags int, mode uint32) (int, error) { 16 for { 17 result, err := unix.Openat(directory, path, flags, mode) 18 if err == unix.EINTR { 19 continue 20 } 21 return result, err 22 } 23 } 24 25 // readRetryingOnEINTR is a wrapper around the read system call that retries on 26 // EINTR errors and returns on the first successful call or non-EINTR error. 27 func readRetryingOnEINTR(file int, buffer []byte) (int, error) { 28 for { 29 result, err := unix.Read(file, buffer) 30 if err == unix.EINTR { 31 continue 32 } else if err == nil && result == 0 { 33 return 0, io.EOF 34 } 35 return result, err 36 } 37 } 38 39 // seekConsideringEINTR is a direct passthrough to the lseek system call that 40 // doesn't retry on EINTR. It's only defined to highlight the intentional 41 // absence of seekRetryingOnEINTR. seekRetryingOnEINTR is left unimplemented 42 // because it would have to handle cases of partially successful seeks (which 43 // would be complicated in the case of SEEK_CUR or other relative whence values) 44 // and because POSIX doesn't specify that lseek can return EINTR. The Go 45 // standard library and runtime also invoke lseek without retrying on EINTR. 46 func seekConsideringEINTR(file int, offset int64, whence int) (int64, error) { 47 return unix.Seek(file, offset, whence) 48 } 49 50 // closeConsideringEINTR is a direct passthrough to the close system call that 51 // doesn't retry on EINTR. It's only defined to highlight the intentional 52 // absence of closeRetryingOnEINTR. closeRetryingOnEINTR is left unimplemented 53 // because POSIX makes no guarantees about the state of a file descriptor in the 54 // event of an EINTR error, and thus retrying closure could lead to a race 55 // condition with file descriptor re-use if the file is, in fact, closed. This 56 // is the same policy adopted by the Go standard library and runtime. 57 func closeConsideringEINTR(file int) error { 58 return unix.Close(file) 59 } 60 61 // mkdiratRetryingOnEINTR is a wrapper around the mkdirat system call that 62 // retries on EINTR errors and returns on the first successful call or non-EINTR 63 // error. 64 func mkdiratRetryingOnEINTR(directory int, path string, mode uint32) error { 65 for { 66 err := unix.Mkdirat(directory, path, mode) 67 if err == unix.EINTR { 68 continue 69 } 70 return err 71 } 72 } 73 74 // renameatRetryingOnEINTR is a wrapper around the renameat system call that 75 // retries on EINTR errors and returns on the first successful call or non-EINTR 76 // error. 77 func renameatRetryingOnEINTR(oldDirectory int, oldPath string, newDirectory int, newPath string) error { 78 for { 79 err := unix.Renameat(oldDirectory, oldPath, newDirectory, newPath) 80 if err == unix.EINTR { 81 continue 82 } 83 return err 84 } 85 } 86 87 // unlinkatRetryingOnEINTR is a wrapper around the unlinkat system call that 88 // retries on EINTR errors and returns on the first successful call or non-EINTR 89 // error. 90 func unlinkatRetryingOnEINTR(directory int, path string, flags int) error { 91 for { 92 err := unix.Unlinkat(directory, path, flags) 93 if err == unix.EINTR { 94 continue 95 } 96 return err 97 } 98 } 99 100 // fstatRetryingOnEINTR is a wrapper around the fstat system call that retries 101 // on EINTR errors and returns on the first successful call or non-EINTR error. 102 func fstatRetryingOnEINTR(file int, metadata *unix.Stat_t) error { 103 for { 104 err := unix.Fstat(file, metadata) 105 if err == unix.EINTR { 106 continue 107 } 108 return err 109 } 110 } 111 112 // fchmodRetryingOnEINTR is a wrapper around the fchmod system call that retries 113 // on EINTR errors and returns on the first successful call or non-EINTR error. 114 func fchmodRetryingOnEINTR(file int, mode uint32) error { 115 for { 116 err := unix.Fchmod(file, mode) 117 if err == unix.EINTR { 118 continue 119 } 120 return err 121 } 122 } 123 124 // fstatatRetryingOnEINTR is a wrapper around the fstatat system call that 125 // retries on EINTR errors and returns on the first successful call or non-EINTR 126 // error. 127 func fstatatRetryingOnEINTR(directory int, path string, metadata *unix.Stat_t, flags int) error { 128 for { 129 err := unix.Fstatat(directory, path, metadata, flags) 130 if err == unix.EINTR { 131 continue 132 } 133 return err 134 } 135 } 136 137 // fchmodatRetryingOnEINTR is a wrapper around the fchmodat system call that 138 // retries on EINTR errors and returns on the first successful call or non-EINTR 139 // error. 140 func fchmodatRetryingOnEINTR(directory int, path string, mode uint32, flags int) error { 141 for { 142 err := unix.Fchmodat(directory, path, mode, flags) 143 if err == unix.EINTR { 144 continue 145 } 146 return err 147 } 148 } 149 150 // fchownatRetryingOnEINTR is a wrapper around the fchownat system call that 151 // retries on EINTR errors and returns on the first successful call or non-EINTR 152 // error. 153 func fchownatRetryingOnEINTR(directory int, path string, uid int, gid int, flags int) error { 154 for { 155 err := unix.Fchownat(directory, path, uid, gid, flags) 156 if err == unix.EINTR { 157 continue 158 } 159 return err 160 } 161 } 162 163 // symlinkatRetryingOnEINTR is a wrapper around the symlinkat system call that 164 // retries on EINTR errors and returns on the first successful call or non-EINTR 165 // error. 166 func symlinkatRetryingOnEINTR(target string, directory int, path string) error { 167 for { 168 err := syscall.Symlinkat(target, directory, path) 169 if err == unix.EINTR { 170 continue 171 } 172 return err 173 } 174 } 175 176 // readlinkatRetryingOnEINTR is a wrapper around the readlinkat system call that 177 // retries on EINTR errors and returns on the first successful call or non-EINTR 178 // error. 179 func readlinkatRetryingOnEINTR(directory int, path string, buffer []byte) (int, error) { 180 for { 181 result, err := syscall.Readlinkat(directory, path, buffer) 182 if err == unix.EINTR { 183 continue 184 } 185 return result, err 186 } 187 }