Initial QSfera import
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
- make check
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
PKGS := github.com/pkg/errors
|
||||
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
|
||||
GO := go
|
||||
|
||||
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
|
||||
|
||||
test:
|
||||
$(GO) test $(PKGS)
|
||||
|
||||
vet: | test
|
||||
$(GO) vet $(PKGS)
|
||||
|
||||
staticcheck:
|
||||
$(GO) get honnef.co/go/tools/cmd/staticcheck
|
||||
staticcheck -checks all $(PKGS)
|
||||
|
||||
misspell:
|
||||
$(GO) get github.com/client9/misspell/cmd/misspell
|
||||
misspell \
|
||||
-locale GB \
|
||||
-error \
|
||||
*.md *.go
|
||||
|
||||
unconvert:
|
||||
$(GO) get github.com/mdempsky/unconvert
|
||||
unconvert -v $(PKGS)
|
||||
|
||||
ineffassign:
|
||||
$(GO) get github.com/gordonklaus/ineffassign
|
||||
find $(SRCDIRS) -name '*.go' | xargs ineffassign
|
||||
|
||||
pedantic: check errcheck
|
||||
|
||||
unparam:
|
||||
$(GO) get mvdan.cc/unparam
|
||||
unparam ./...
|
||||
|
||||
errcheck:
|
||||
$(GO) get github.com/kisielk/errcheck
|
||||
errcheck $(PKGS)
|
||||
|
||||
gofmt:
|
||||
@echo Checking code is gofmted
|
||||
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||
|
||||
## Adding context to an error
|
||||
|
||||
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## Retrieving the cause of an error
|
||||
|
||||
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Roadmap
|
||||
|
||||
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
|
||||
|
||||
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
|
||||
- 1.0. Final release.
|
||||
|
||||
## Contributing
|
||||
|
||||
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
|
||||
|
||||
Before sending a PR, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d\n", f, f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withStack) Unwrap() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (w *withMessage) Unwrap() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// +build go1.13
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
)
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
func Is(err, target error) bool { return stderrors.Is(err, target) }
|
||||
|
||||
// As finds the first error in err's chain that matches target, and if so, sets
|
||||
// target to that error value and returns true.
|
||||
//
|
||||
// The chain consists of err itself followed by the sequence of errors obtained by
|
||||
// repeatedly calling Unwrap.
|
||||
//
|
||||
// An error matches target if the error's concrete value is assignable to the value
|
||||
// pointed to by target, or if the error has a method As(interface{}) bool such that
|
||||
// As(target) returns true. In the latter case, the As method is responsible for
|
||||
// setting target.
|
||||
//
|
||||
// As will panic if target is not a non-nil pointer to either a type that implements
|
||||
// error, or to any interface type. As returns false if err is nil.
|
||||
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's
|
||||
// type contains an Unwrap method returning error.
|
||||
// Otherwise, Unwrap returns nil.
|
||||
func Unwrap(err error) error {
|
||||
return stderrors.Unwrap(err)
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
// For historical reasons if Frame is interpreted as a uintptr
|
||||
// its value represents the program counter + 1.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// name returns the name of this function, if known.
|
||||
func (f Frame) name() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return fn.Name()
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
io.WriteString(s, f.name())
|
||||
io.WriteString(s, "\n\t")
|
||||
io.WriteString(s, f.file())
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
io.WriteString(s, strconv.Itoa(f.line()))
|
||||
case 'n':
|
||||
io.WriteString(s, funcname(f.name()))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText formats a stacktrace Frame as a text string. The output is the
|
||||
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
|
||||
func (f Frame) MarshalText() ([]byte, error) {
|
||||
name := f.name()
|
||||
if name == "unknown" {
|
||||
return []byte(name), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
io.WriteString(s, "\n")
|
||||
f.Format(s, verb)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
case 's':
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// formatSlice will format this StackTrace into the given buffer as a slice of
|
||||
// Frame, only valid when called with '%s' or '%v'.
|
||||
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
|
||||
io.WriteString(s, "[")
|
||||
for i, f := range st {
|
||||
if i > 0 {
|
||||
io.WriteString(s, " ")
|
||||
}
|
||||
f.Format(s, verb)
|
||||
}
|
||||
io.WriteString(s, "]")
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.DS_Store
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
*.swp
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2012 Dave Cheney. All rights reserved.
|
||||
Copyright (c) 2014 Kuba Podgórski. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
[](http://godoc.org/github.com/pkg/xattr)
|
||||
[](https://goreportcard.com/report/github.com/pkg/xattr)
|
||||
[](https://github.com/pkg/xattr/actions?query=workflow%3Abuild)
|
||||
[](https://codecov.io/gh/pkg/xattr)
|
||||
|
||||
xattr
|
||||
=====
|
||||
Extended attribute support for Go (linux + darwin + freebsd + netbsd + solaris).
|
||||
|
||||
"Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process. An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty." [See more...](https://en.wikipedia.org/wiki/Extended_file_attributes)
|
||||
|
||||
`SetWithFlags` allows to additionally pass system flags to be forwarded to the underlying calls. FreeBSD and NetBSD do not support this and the parameter will be ignored.
|
||||
|
||||
The `L` variants of all functions (`LGet/LSet/...`) are identical to `Get/Set/...` except that they
|
||||
do not reference a symlink that appears at the end of a path. See
|
||||
[GoDoc](http://godoc.org/github.com/pkg/xattr) for details.
|
||||
|
||||
### Example
|
||||
```go
|
||||
const path = "/tmp/myfile"
|
||||
const prefix = "user."
|
||||
|
||||
if err := xattr.Set(path, prefix+"test", []byte("test-attr-value")); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var list []string
|
||||
if list, err = xattr.List(path); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
if data, err = xattr.Get(path, prefix+"test"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = xattr.Remove(path, prefix+"test"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// One can also specify the flags parameter to be passed to the OS.
|
||||
if err := xattr.SetWithFlags(path, prefix+"test", []byte("test-attr-value"), xattr.XATTR_CREATE); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
Package xattr provides support for extended attributes on linux, darwin and freebsd.
|
||||
Extended attributes are name:value pairs associated permanently with files and directories,
|
||||
similar to the environment strings associated with a process.
|
||||
An attribute may be defined or undefined. If it is defined, its value may be empty or non-empty.
|
||||
More details you can find here: https://en.wikipedia.org/wiki/Extended_file_attributes .
|
||||
|
||||
All functions are provided in triples: Get/LGet/FGet, Set/LSet/FSet etc. The "L"
|
||||
variant will not follow a symlink at the end of the path, and "F" variant accepts
|
||||
a file descriptor instead of a path.
|
||||
|
||||
Example for "L" variant, assuming path is "/symlink1/symlink2", where both components are
|
||||
symlinks:
|
||||
Get will follow "symlink1" and "symlink2" and operate on the target of
|
||||
"symlink2". LGet will follow "symlink1" but operate directly on "symlink2".
|
||||
*/
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Error records an error and the operation, file path and attribute that caused it.
|
||||
type Error struct {
|
||||
Op string
|
||||
Path string
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error { return e.Err }
|
||||
|
||||
func (e *Error) Error() (errstr string) {
|
||||
if e.Op != "" {
|
||||
errstr += e.Op
|
||||
}
|
||||
if e.Path != "" {
|
||||
if errstr != "" {
|
||||
errstr += " "
|
||||
}
|
||||
errstr += e.Path
|
||||
}
|
||||
if e.Name != "" {
|
||||
if errstr != "" {
|
||||
errstr += " "
|
||||
}
|
||||
errstr += e.Name
|
||||
}
|
||||
if e.Err != nil {
|
||||
if errstr != "" {
|
||||
errstr += ": "
|
||||
}
|
||||
errstr += e.Err.Error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get retrieves extended attribute data associated with path. It will follow
|
||||
// all symlinks along the path.
|
||||
func Get(path, name string) ([]byte, error) {
|
||||
return get(path, name, func(name string, data []byte) (int, error) {
|
||||
return getxattr(path, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
// LGet is like Get but does not follow a symlink at the end of the path.
|
||||
func LGet(path, name string) ([]byte, error) {
|
||||
return get(path, name, func(name string, data []byte) (int, error) {
|
||||
return lgetxattr(path, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
// FGet is like Get but accepts a os.File instead of a file path.
|
||||
func FGet(f *os.File, name string) ([]byte, error) {
|
||||
return get(f.Name(), name, func(name string, data []byte) (int, error) {
|
||||
return fgetxattr(f, name, data)
|
||||
})
|
||||
}
|
||||
|
||||
type getxattrFunc func(name string, data []byte) (int, error)
|
||||
|
||||
// get contains the buffer allocation logic used by both Get and LGet.
|
||||
func get(path string, name string, getxattrFunc getxattrFunc) ([]byte, error) {
|
||||
const (
|
||||
// Start with a 1 KB buffer for the xattr value
|
||||
initialBufSize = 1024
|
||||
|
||||
// The theoretical maximum xattr value size on MacOS is 64 MB. On Linux it's
|
||||
// much smaller: documented at 64 KB. However, at least on TrueNAS SCALE, a
|
||||
// Debian-based Linux distro, it can be larger.
|
||||
maxBufSize = 64 * 1024 * 1024
|
||||
|
||||
// Function name as reported in error messages
|
||||
myname = "xattr.get"
|
||||
)
|
||||
|
||||
size := initialBufSize
|
||||
for {
|
||||
data := make([]byte, size)
|
||||
read, err := getxattrFunc(name, data)
|
||||
|
||||
// If the buffer was too small to fit the value, Linux and MacOS react
|
||||
// differently:
|
||||
// Linux: returns an ERANGE error and "-1" bytes. However, the TrueNAS
|
||||
// SCALE distro sometimes returns E2BIG.
|
||||
// MacOS: truncates the value and returns "size" bytes. If the value
|
||||
// happens to be exactly as big as the buffer, we cannot know if it was
|
||||
// truncated, and we retry with a bigger buffer. Contrary to documentation,
|
||||
// MacOS never seems to return ERANGE!
|
||||
// To keep the code simple, we always check both conditions, and sometimes
|
||||
// double the buffer size without it being strictly necessary.
|
||||
if err == syscall.ERANGE || err == syscall.E2BIG || read == size {
|
||||
// The buffer was too small. Try again.
|
||||
size <<= 1
|
||||
if size >= maxBufSize {
|
||||
return nil, &Error{myname, path, name, syscall.EOVERFLOW}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, name, err}
|
||||
}
|
||||
return data[:read], nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set associates name and data together as an attribute of path.
|
||||
func Set(path, name string, data []byte) error {
|
||||
if err := setxattr(path, name, data, 0); err != nil {
|
||||
return &Error{"xattr.Set", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LSet is like Set but does not follow a symlink at
|
||||
// the end of the path.
|
||||
func LSet(path, name string, data []byte) error {
|
||||
if err := lsetxattr(path, name, data, 0); err != nil {
|
||||
return &Error{"xattr.LSet", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSet is like Set but accepts a os.File instead of a file path.
|
||||
func FSet(f *os.File, name string, data []byte) error {
|
||||
if err := fsetxattr(f, name, data, 0); err != nil {
|
||||
return &Error{"xattr.FSet", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWithFlags associates name and data together as an attribute of path.
|
||||
// Forwards the flags parameter to the syscall layer.
|
||||
func SetWithFlags(path, name string, data []byte, flags int) error {
|
||||
if err := setxattr(path, name, data, flags); err != nil {
|
||||
return &Error{"xattr.SetWithFlags", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LSetWithFlags is like SetWithFlags but does not follow a symlink at
|
||||
// the end of the path.
|
||||
func LSetWithFlags(path, name string, data []byte, flags int) error {
|
||||
if err := lsetxattr(path, name, data, flags); err != nil {
|
||||
return &Error{"xattr.LSetWithFlags", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FSetWithFlags is like SetWithFlags but accepts a os.File instead of a file path.
|
||||
func FSetWithFlags(f *os.File, name string, data []byte, flags int) error {
|
||||
if err := fsetxattr(f, name, data, flags); err != nil {
|
||||
return &Error{"xattr.FSetWithFlags", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the attribute associated with the given path.
|
||||
func Remove(path, name string) error {
|
||||
if err := removexattr(path, name); err != nil {
|
||||
return &Error{"xattr.Remove", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LRemove is like Remove but does not follow a symlink at the end of the
|
||||
// path.
|
||||
func LRemove(path, name string) error {
|
||||
if err := lremovexattr(path, name); err != nil {
|
||||
return &Error{"xattr.LRemove", path, name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FRemove is like Remove but accepts a os.File instead of a file path.
|
||||
func FRemove(f *os.File, name string) error {
|
||||
if err := fremovexattr(f, name); err != nil {
|
||||
return &Error{"xattr.FRemove", f.Name(), name, err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List retrieves a list of names of extended attributes associated
|
||||
// with the given path in the file system.
|
||||
func List(path string) ([]string, error) {
|
||||
return list(path, func(data []byte) (int, error) {
|
||||
return listxattr(path, data)
|
||||
})
|
||||
}
|
||||
|
||||
// LList is like List but does not follow a symlink at the end of the
|
||||
// path.
|
||||
func LList(path string) ([]string, error) {
|
||||
return list(path, func(data []byte) (int, error) {
|
||||
return llistxattr(path, data)
|
||||
})
|
||||
}
|
||||
|
||||
// FList is like List but accepts a os.File instead of a file path.
|
||||
func FList(f *os.File) ([]string, error) {
|
||||
return list(f.Name(), func(data []byte) (int, error) {
|
||||
return flistxattr(f, data)
|
||||
})
|
||||
}
|
||||
|
||||
type listxattrFunc func(data []byte) (int, error)
|
||||
|
||||
// list contains the buffer allocation logic used by both List and LList.
|
||||
func list(path string, listxattrFunc listxattrFunc) ([]string, error) {
|
||||
myname := "xattr.list"
|
||||
// find size.
|
||||
size, err := listxattrFunc(nil)
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, "", err}
|
||||
}
|
||||
if size > 0 {
|
||||
// `size + 1` because of ERANGE error when reading
|
||||
// from a SMB1 mount point (https://github.com/pkg/xattr/issues/16).
|
||||
buf := make([]byte, size+1)
|
||||
// Read into buffer of that size.
|
||||
read, err := listxattrFunc(buf)
|
||||
if err != nil {
|
||||
return nil, &Error{myname, path, "", err}
|
||||
}
|
||||
return stringsFromByteSlice(buf[:read]), nil
|
||||
}
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
// bytePtrFromSlice returns a pointer to array of bytes and a size.
|
||||
func bytePtrFromSlice(data []byte) (ptr *byte, size int) {
|
||||
size = len(data)
|
||||
if size > 0 {
|
||||
ptr = &data[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
//go:build freebsd || netbsd
|
||||
// +build freebsd netbsd
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
EXTATTR_NAMESPACE_USER = 1
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENOATTR
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return sysGet(syscall.SYS_EXTATTR_GET_FILE, path, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return sysGet(syscall.SYS_EXTATTR_GET_LINK, path, name, data)
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
return getxattr(f.Name(), name, data)
|
||||
}
|
||||
|
||||
// sysGet is called by getxattr and lgetxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysGet(syscallNum uintptr, path string, name string, data []byte) (int, error) {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_get_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
void *data,
|
||||
size_t nbytes);
|
||||
|
||||
ssize_t extattr_get_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
void *data,
|
||||
size_t nbytes);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return int(r0), err
|
||||
}
|
||||
return int(r0), nil
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return sysSet(syscall.SYS_EXTATTR_SET_FILE, path, name, data)
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return sysSet(syscall.SYS_EXTATTR_SET_LINK, path, name, data)
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return setxattr(f.Name(), name, data, flags)
|
||||
}
|
||||
|
||||
// sysSet is called by setxattr and lsetxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysSet(syscallNum uintptr, path string, name string, data []byte) error {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_set_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
const void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
|
||||
ssize_t extattr_set_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname,
|
||||
const void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
if int(r0) != nbytes {
|
||||
return syscall.E2BIG
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return sysRemove(syscall.SYS_EXTATTR_DELETE_FILE, path, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return sysRemove(syscall.SYS_EXTATTR_DELETE_LINK, path, name)
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return removexattr(f.Name(), name)
|
||||
}
|
||||
|
||||
// sysSet is called by removexattr and lremovexattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysRemove(syscallNum uintptr, path string, name string) error {
|
||||
/*
|
||||
int extattr_delete_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname
|
||||
);
|
||||
|
||||
int extattr_delete_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
const char *attrname
|
||||
);
|
||||
*/
|
||||
_, _, err := syscall.Syscall(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(syscall.StringBytePtr(name))),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return sysList(syscall.SYS_EXTATTR_LIST_FILE, path, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return sysList(syscall.SYS_EXTATTR_LIST_LINK, path, data)
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
return listxattr(f.Name(), data)
|
||||
}
|
||||
|
||||
// sysSet is called by listxattr and llistxattr with the appropriate syscall
|
||||
// number. This works because syscalls have the same signature and return
|
||||
// values.
|
||||
func sysList(syscallNum uintptr, path string, data []byte) (int, error) {
|
||||
ptr, nbytes := bytePtrFromSlice(data)
|
||||
/*
|
||||
ssize_t extattr_list_file(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
|
||||
ssize_t extattr_list_link(
|
||||
const char *path,
|
||||
int attrnamespace,
|
||||
void *data,
|
||||
size_t nbytes
|
||||
);
|
||||
*/
|
||||
r0, _, err := syscall.Syscall6(syscallNum, uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
|
||||
EXTATTR_NAMESPACE_USER, uintptr(unsafe.Pointer(ptr)), uintptr(nbytes), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return int(r0), err
|
||||
}
|
||||
return int(r0), nil
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On FreeBSD, each entry consists of a single byte containing the length
|
||||
// of the attribute name, followed by the attribute name.
|
||||
// The name is _not_ terminated by NULL.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
index := 0
|
||||
for index < len(buf) {
|
||||
next := index + 1 + int(buf[index])
|
||||
result = append(result, string(buf[index+1:next]))
|
||||
index = next
|
||||
}
|
||||
return
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_NOFOLLOW = 0x0001
|
||||
XATTR_CREATE = 0x0002
|
||||
XATTR_REPLACE = 0x0004
|
||||
XATTR_NOSECURITY = 0x0008
|
||||
XATTR_NODEFAULT = 0x0010
|
||||
XATTR_SHOWCOMPRESSION = 0x0020
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENOATTR
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return unix.Getxattr(path, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return unix.Lgetxattr(path, name, data)
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
path, err := getPath(f)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getxattr(path, name, data)
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.Setxattr(path, name, data, flags)
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.Lsetxattr(path, name, data, flags)
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
path, err := getPath(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setxattr(path, name, data, flags)
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return unix.Removexattr(path, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return unix.Lremovexattr(path, name)
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
path, err := getPath(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return removexattr(path, name)
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return unix.Listxattr(path, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return unix.Llistxattr(path, data)
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
path, err := getPath(f)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return listxattr(path, data)
|
||||
}
|
||||
|
||||
// getPath returns the full path to the specified file.
|
||||
func getPath(f *os.File) (string, error) {
|
||||
var buf [unix.PathMax]byte
|
||||
_, _, err := unix.Syscall(unix.SYS_FCNTL,
|
||||
uintptr(int(f.Fd())),
|
||||
uintptr(unix.F_GETPATH),
|
||||
uintptr(unsafe.Pointer(&buf[0])))
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
n := 0
|
||||
for n < len(buf) && buf[n] != 0 {
|
||||
n++
|
||||
}
|
||||
return string(buf[:n]), nil
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On Darwin and Linux, each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_CREATE = unix.XATTR_CREATE
|
||||
XATTR_REPLACE = unix.XATTR_REPLACE
|
||||
|
||||
// ENOATTR is not exported by the syscall package on Linux, because it is
|
||||
// an alias for ENODATA. We export it here so it is available on all
|
||||
// our supported platforms.
|
||||
ENOATTR = syscall.ENODATA
|
||||
)
|
||||
|
||||
// On Linux, FUSE and CIFS filesystems can return EINTR for interrupted system
|
||||
// calls. This function works around this by retrying system calls until they
|
||||
// stop returning EINTR.
|
||||
//
|
||||
// See https://github.com/golang/go/commit/6b420169d798c7ebe733487b56ea5c3fa4aab5ce.
|
||||
func ignoringEINTR(fn func() error) (err error) {
|
||||
for {
|
||||
err = fn()
|
||||
if err != unix.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Getxattr(path, name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Lgetxattr(path, name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Fgetxattr(int(f.Fd()), name, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Setxattr(path, name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Lsetxattr(path, name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Fsetxattr(int(f.Fd()), name, data, flags)
|
||||
})
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Removexattr(path, name)
|
||||
})
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Lremovexattr(path, name)
|
||||
})
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return ignoringEINTR(func() (err error) {
|
||||
return unix.Fremovexattr(int(f.Fd()), name)
|
||||
})
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Listxattr(path, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Llistxattr(path, data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
var r int
|
||||
err := ignoringEINTR(func() (err error) {
|
||||
r, err = unix.Flistxattr(int(f.Fd()), data)
|
||||
return err
|
||||
})
|
||||
return r, err
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// On Darwin and Linux, each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
XATTR_SUPPORTED = true
|
||||
|
||||
XATTR_CREATE = 0x1
|
||||
XATTR_REPLACE = 0x2
|
||||
|
||||
// ENOATTR is not defined on Solaris. When attempting to open an
|
||||
// extended attribute that doesn't exist, we'll get ENOENT. For
|
||||
// compatibility with other platforms, we make ENOATTR available as
|
||||
// an alias of unix.ENOENT.
|
||||
ENOATTR = unix.ENOENT
|
||||
)
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
f, err := openNonblock(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return fgetxattr(f, name, data)
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
fd, err := unix.Openat(int(f.Fd()), name, unix.O_RDONLY|unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = unix.Close(fd)
|
||||
}()
|
||||
return unix.Read(fd, data)
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
f, err := openNonblock(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fsetxattr(f, name, data, flags)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
mode := unix.O_WRONLY | unix.O_XATTR
|
||||
if flags&XATTR_REPLACE != 0 {
|
||||
mode |= unix.O_TRUNC
|
||||
} else if flags&XATTR_CREATE != 0 {
|
||||
mode |= unix.O_CREAT | unix.O_EXCL
|
||||
} else {
|
||||
mode |= unix.O_CREAT | unix.O_TRUNC
|
||||
}
|
||||
fd, err := unix.Openat(int(f.Fd()), name, mode, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = unix.Write(fd, data); err != nil {
|
||||
_ = unix.Close(fd)
|
||||
return err
|
||||
}
|
||||
return unix.Close(fd)
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
mode := unix.O_RDONLY | unix.O_XATTR | unix.O_NONBLOCK | unix.O_CLOEXEC
|
||||
fd, err := unix.Open(path, mode, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f := os.NewFile(uintptr(fd), path)
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return fremovexattr(f, name)
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return unix.ENOTSUP
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
fd, err := unix.Openat(int(f.Fd()), ".", unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = unix.Close(fd)
|
||||
}()
|
||||
return unix.Unlinkat(fd, name, 0)
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
f, err := openNonblock(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
return flistxattr(f, data)
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return 0, unix.ENOTSUP
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
fd, err := unix.Openat(int(f.Fd()), ".", unix.O_RDONLY|unix.O_XATTR, 0)
|
||||
if err != nil {
|
||||
// When attempting to list extended attributes on a filesystem
|
||||
// that doesn't support them (like as UFS and tmpfs), we'll get
|
||||
// EINVAL. Translate this error to the more conventional ENOTSUP.
|
||||
if errors.Is(err, unix.EINVAL) {
|
||||
return 0, unix.ENOTSUP
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
xf := os.NewFile(uintptr(fd), f.Name())
|
||||
defer func() {
|
||||
_ = xf.Close()
|
||||
}()
|
||||
names, err := xf.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var buf []byte
|
||||
for _, name := range names {
|
||||
buf = append(buf, append([]byte(name), '\000')...)
|
||||
}
|
||||
if data == nil {
|
||||
return len(buf), nil
|
||||
}
|
||||
return copy(data, buf), nil
|
||||
}
|
||||
|
||||
// Like os.Open, but passes O_NONBLOCK to the open(2) syscall.
|
||||
func openNonblock(path string) (*os.File, error) {
|
||||
fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fd), path), err
|
||||
}
|
||||
|
||||
// stringsFromByteSlice converts a sequence of attributes to a []string.
|
||||
// We simulate Linux/Darwin, where each entry is a NULL-terminated string.
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
offset := 0
|
||||
for index, b := range buf {
|
||||
if b == 0 {
|
||||
result = append(result, string(buf[offset:index]))
|
||||
offset = index + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
//go:build !linux && !freebsd && !netbsd && !darwin && !solaris
|
||||
// +build !linux,!freebsd,!netbsd,!darwin,!solaris
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// We need to use the default for non supported operating systems
|
||||
ENOATTR = syscall.Errno(0x59)
|
||||
)
|
||||
|
||||
// XATTR_SUPPORTED will be true if the current platform is supported
|
||||
const XATTR_SUPPORTED = false
|
||||
|
||||
func getxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func lgetxattr(path string, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func fgetxattr(f *os.File, name string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func setxattr(path string, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func lsetxattr(path string, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fsetxattr(f *os.File, name string, data []byte, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removexattr(path string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func lremovexattr(path string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fremovexattr(f *os.File, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listxattr(path string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func llistxattr(path string, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func flistxattr(f *os.File, data []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// dummy
|
||||
func stringsFromByteSlice(buf []byte) (result []string) {
|
||||
return []string{}
|
||||
}
|
||||
Reference in New Issue
Block a user