123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- package regstate
- import (
- "encoding/json"
- "fmt"
- "net/url"
- "os"
- "path/filepath"
- "reflect"
- "syscall"
- "golang.org/x/sys/windows/registry"
- )
- //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go regstate.go
- //sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
- const (
- _REG_OPTION_VOLATILE = 1
- _REG_CREATED_NEW_KEY = 1
- _REG_OPENED_EXISTING_KEY = 2
- )
- type Key struct {
- registry.Key
- Name string
- }
- var localMachine = &Key{registry.LOCAL_MACHINE, "HKEY_LOCAL_MACHINE"}
- var localUser = &Key{registry.CURRENT_USER, "HKEY_CURRENT_USER"}
- var rootPath = `SOFTWARE\Microsoft\runhcs`
- type NotFoundError struct {
- Id string
- }
- func (err *NotFoundError) Error() string {
- return fmt.Sprintf("ID '%s' was not found", err.Id)
- }
- func IsNotFoundError(err error) bool {
- _, ok := err.(*NotFoundError)
- return ok
- }
- type NoStateError struct {
- ID string
- Key string
- }
- func (err *NoStateError) Error() string {
- return fmt.Sprintf("state '%s' is not present for ID '%s'", err.Key, err.ID)
- }
- func createVolatileKey(k *Key, path string, access uint32) (newk *Key, openedExisting bool, err error) {
- var (
- h syscall.Handle
- d uint32
- )
- fullpath := filepath.Join(k.Name, path)
- err = regCreateKeyEx(syscall.Handle(k.Key), syscall.StringToUTF16Ptr(path), 0, nil, _REG_OPTION_VOLATILE, access, nil, &h, &d)
- if err != nil {
- return nil, false, &os.PathError{Op: "RegCreateKeyEx", Path: fullpath, Err: err}
- }
- return &Key{registry.Key(h), fullpath}, d == _REG_OPENED_EXISTING_KEY, nil
- }
- func hive(perUser bool) *Key {
- r := localMachine
- if perUser {
- r = localUser
- }
- return r
- }
- func Open(root string, perUser bool) (*Key, error) {
- k, _, err := createVolatileKey(hive(perUser), rootPath, registry.ALL_ACCESS)
- if err != nil {
- return nil, err
- }
- defer k.Close()
- k2, _, err := createVolatileKey(k, url.PathEscape(root), registry.ALL_ACCESS)
- if err != nil {
- return nil, err
- }
- return k2, nil
- }
- func RemoveAll(root string, perUser bool) error {
- k, err := hive(perUser).open(rootPath)
- if err != nil {
- return err
- }
- defer k.Close()
- r, err := k.open(url.PathEscape(root))
- if err != nil {
- return err
- }
- defer r.Close()
- ids, err := r.Enumerate()
- if err != nil {
- return err
- }
- for _, id := range ids {
- err = r.Remove(id)
- if err != nil {
- return err
- }
- }
- r.Close()
- return k.Remove(root)
- }
- func (k *Key) Close() error {
- err := k.Key.Close()
- k.Key = 0
- return err
- }
- func (k *Key) Enumerate() ([]string, error) {
- escapedIDs, err := k.ReadSubKeyNames(0)
- if err != nil {
- return nil, err
- }
- var ids []string
- for _, e := range escapedIDs {
- id, err := url.PathUnescape(e)
- if err == nil {
- ids = append(ids, id)
- }
- }
- return ids, nil
- }
- func (k *Key) open(name string) (*Key, error) {
- fullpath := filepath.Join(k.Name, name)
- nk, err := registry.OpenKey(k.Key, name, registry.ALL_ACCESS)
- if err != nil {
- return nil, &os.PathError{Op: "RegOpenKey", Path: fullpath, Err: err}
- }
- return &Key{nk, fullpath}, nil
- }
- func (k *Key) openid(id string) (*Key, error) {
- escaped := url.PathEscape(id)
- fullpath := filepath.Join(k.Name, escaped)
- nk, err := k.open(escaped)
- if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
- return nil, &NotFoundError{id}
- }
- if err != nil {
- return nil, &os.PathError{Op: "RegOpenKey", Path: fullpath, Err: err}
- }
- return nk, nil
- }
- func (k *Key) Remove(id string) error {
- escaped := url.PathEscape(id)
- err := registry.DeleteKey(k.Key, escaped)
- if err != nil {
- if err == syscall.ERROR_FILE_NOT_FOUND {
- return &NotFoundError{id}
- }
- return &os.PathError{Op: "RegDeleteKey", Path: filepath.Join(k.Name, escaped), Err: err}
- }
- return nil
- }
- func (k *Key) set(id string, create bool, key string, state interface{}) error {
- var sk *Key
- var err error
- if create {
- var existing bool
- eid := url.PathEscape(id)
- sk, existing, err = createVolatileKey(k, eid, registry.ALL_ACCESS)
- if err != nil {
- return err
- }
- defer sk.Close()
- if existing {
- sk.Close()
- return fmt.Errorf("container %s already exists", id)
- }
- } else {
- sk, err = k.openid(id)
- if err != nil {
- return err
- }
- defer sk.Close()
- }
- switch reflect.TypeOf(state).Kind() {
- case reflect.Bool:
- v := uint32(0)
- if state.(bool) {
- v = 1
- }
- err = sk.SetDWordValue(key, v)
- case reflect.Int:
- err = sk.SetQWordValue(key, uint64(state.(int)))
- case reflect.String:
- err = sk.SetStringValue(key, state.(string))
- default:
- var js []byte
- js, err = json.Marshal(state)
- if err != nil {
- return err
- }
- err = sk.SetBinaryValue(key, js)
- }
- if err != nil {
- if err == syscall.ERROR_FILE_NOT_FOUND {
- return &NoStateError{id, key}
- }
- return &os.PathError{Op: "RegSetValueEx", Path: sk.Name + ":" + key, Err: err}
- }
- return nil
- }
- func (k *Key) Create(id, key string, state interface{}) error {
- return k.set(id, true, key, state)
- }
- func (k *Key) Set(id, key string, state interface{}) error {
- return k.set(id, false, key, state)
- }
- func (k *Key) Clear(id, key string) error {
- sk, err := k.openid(id)
- if err != nil {
- return err
- }
- defer sk.Close()
- err = sk.DeleteValue(key)
- if err != nil {
- if err == syscall.ERROR_FILE_NOT_FOUND {
- return &NoStateError{id, key}
- }
- return &os.PathError{Op: "RegDeleteValue", Path: sk.Name + ":" + key, Err: err}
- }
- return nil
- }
- func (k *Key) Get(id, key string, state interface{}) error {
- sk, err := k.openid(id)
- if err != nil {
- return err
- }
- defer sk.Close()
- var js []byte
- switch reflect.TypeOf(state).Elem().Kind() {
- case reflect.Bool:
- var v uint64
- v, _, err = sk.GetIntegerValue(key)
- if err == nil {
- *state.(*bool) = v != 0
- }
- case reflect.Int:
- var v uint64
- v, _, err = sk.GetIntegerValue(key)
- if err == nil {
- *state.(*int) = int(v)
- }
- case reflect.String:
- var v string
- v, _, err = sk.GetStringValue(key)
- if err == nil {
- *state.(*string) = string(v)
- }
- default:
- js, _, err = sk.GetBinaryValue(key)
- }
- if err != nil {
- if err == syscall.ERROR_FILE_NOT_FOUND {
- return &NoStateError{id, key}
- }
- return &os.PathError{Op: "RegQueryValueEx", Path: sk.Name + ":" + key, Err: err}
- }
- if js != nil {
- err = json.Unmarshal(js, state)
- }
- return err
- }
|