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  }