1
2
3
4
5
6
7 package registry_test
8
9 import (
10 "bytes"
11 "crypto/rand"
12 "os"
13 "syscall"
14 "testing"
15 "time"
16 "unsafe"
17
18 "golang.org/x/sys/windows/registry"
19 )
20
21 func randKeyName(prefix string) string {
22 const numbers = "0123456789"
23 buf := make([]byte, 10)
24 rand.Read(buf)
25 for i, b := range buf {
26 buf[i] = numbers[b%byte(len(numbers))]
27 }
28 return prefix + string(buf)
29 }
30
31 func TestReadSubKeyNames(t *testing.T) {
32 k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS)
33 if err != nil {
34 t.Fatal(err)
35 }
36 defer k.Close()
37
38 names, err := k.ReadSubKeyNames(-1)
39 if err != nil {
40 t.Fatal(err)
41 }
42 var foundStdOle bool
43 for _, name := range names {
44
45 if name == "{00020430-0000-0000-C000-000000000046}" {
46 foundStdOle = true
47 }
48 }
49 if !foundStdOle {
50 t.Fatal("could not find stdole 2.0 OLE Automation")
51 }
52 }
53
54 func TestCreateOpenDeleteKey(t *testing.T) {
55 k, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
56 if err != nil {
57 t.Fatal(err)
58 }
59 defer k.Close()
60
61 testKName := randKeyName("TestCreateOpenDeleteKey_")
62
63 testK, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
64 if err != nil {
65 t.Fatal(err)
66 }
67 defer testK.Close()
68
69 if exist {
70 t.Fatalf("key %q already exists", testKName)
71 }
72
73 testKAgain, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
74 if err != nil {
75 t.Fatal(err)
76 }
77 defer testKAgain.Close()
78
79 if !exist {
80 t.Fatalf("key %q should already exist", testKName)
81 }
82
83 testKOpened, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
84 if err != nil {
85 t.Fatal(err)
86 }
87 defer testKOpened.Close()
88
89 err = registry.DeleteKey(k, testKName)
90 if err != nil {
91 t.Fatal(err)
92 }
93
94 testKOpenedAgain, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
95 if err == nil {
96 defer testKOpenedAgain.Close()
97 t.Fatalf("key %q should already been deleted", testKName)
98 }
99 if err != registry.ErrNotExist {
100 t.Fatalf(`unexpected error ("not exist" expected): %v`, err)
101 }
102 }
103
104 func equalStringSlice(a, b []string) bool {
105 if len(a) != len(b) {
106 return false
107 }
108 if a == nil {
109 return true
110 }
111 for i := range a {
112 if a[i] != b[i] {
113 return false
114 }
115 }
116 return true
117 }
118
119 type ValueTest struct {
120 Type uint32
121 Name string
122 Value interface{}
123 WillFail bool
124 }
125
126 var ValueTests = []ValueTest{
127 {Type: registry.SZ, Name: "String1", Value: ""},
128 {Type: registry.SZ, Name: "String2", Value: "\000", WillFail: true},
129 {Type: registry.SZ, Name: "String3", Value: "Hello World"},
130 {Type: registry.SZ, Name: "String4", Value: "Hello World\000", WillFail: true},
131 {Type: registry.EXPAND_SZ, Name: "ExpString1", Value: ""},
132 {Type: registry.EXPAND_SZ, Name: "ExpString2", Value: "\000", WillFail: true},
133 {Type: registry.EXPAND_SZ, Name: "ExpString3", Value: "Hello World"},
134 {Type: registry.EXPAND_SZ, Name: "ExpString4", Value: "Hello\000World", WillFail: true},
135 {Type: registry.EXPAND_SZ, Name: "ExpString5", Value: "%PATH%"},
136 {Type: registry.EXPAND_SZ, Name: "ExpString6", Value: "%NO_SUCH_VARIABLE%"},
137 {Type: registry.EXPAND_SZ, Name: "ExpString7", Value: "%PATH%;."},
138 {Type: registry.BINARY, Name: "Binary1", Value: []byte{}},
139 {Type: registry.BINARY, Name: "Binary2", Value: []byte{1, 2, 3}},
140 {Type: registry.BINARY, Name: "Binary3", Value: []byte{3, 2, 1, 0, 1, 2, 3}},
141 {Type: registry.DWORD, Name: "Dword1", Value: uint64(0)},
142 {Type: registry.DWORD, Name: "Dword2", Value: uint64(1)},
143 {Type: registry.DWORD, Name: "Dword3", Value: uint64(0xff)},
144 {Type: registry.DWORD, Name: "Dword4", Value: uint64(0xffff)},
145 {Type: registry.QWORD, Name: "Qword1", Value: uint64(0)},
146 {Type: registry.QWORD, Name: "Qword2", Value: uint64(1)},
147 {Type: registry.QWORD, Name: "Qword3", Value: uint64(0xff)},
148 {Type: registry.QWORD, Name: "Qword4", Value: uint64(0xffff)},
149 {Type: registry.QWORD, Name: "Qword5", Value: uint64(0xffffff)},
150 {Type: registry.QWORD, Name: "Qword6", Value: uint64(0xffffffff)},
151 {Type: registry.MULTI_SZ, Name: "MultiString1", Value: []string{"a", "b", "c"}},
152 {Type: registry.MULTI_SZ, Name: "MultiString2", Value: []string{"abc", "", "cba"}},
153 {Type: registry.MULTI_SZ, Name: "MultiString3", Value: []string{""}},
154 {Type: registry.MULTI_SZ, Name: "MultiString4", Value: []string{"abcdef"}},
155 {Type: registry.MULTI_SZ, Name: "MultiString5", Value: []string{"\000"}, WillFail: true},
156 {Type: registry.MULTI_SZ, Name: "MultiString6", Value: []string{"a\000b"}, WillFail: true},
157 {Type: registry.MULTI_SZ, Name: "MultiString7", Value: []string{"ab", "\000", "cd"}, WillFail: true},
158 {Type: registry.MULTI_SZ, Name: "MultiString8", Value: []string{"\000", "cd"}, WillFail: true},
159 {Type: registry.MULTI_SZ, Name: "MultiString9", Value: []string{"ab", "\000"}, WillFail: true},
160 }
161
162 func setValues(t *testing.T, k registry.Key) {
163 for _, test := range ValueTests {
164 var err error
165 switch test.Type {
166 case registry.SZ:
167 err = k.SetStringValue(test.Name, test.Value.(string))
168 case registry.EXPAND_SZ:
169 err = k.SetExpandStringValue(test.Name, test.Value.(string))
170 case registry.MULTI_SZ:
171 err = k.SetStringsValue(test.Name, test.Value.([]string))
172 case registry.BINARY:
173 err = k.SetBinaryValue(test.Name, test.Value.([]byte))
174 case registry.DWORD:
175 err = k.SetDWordValue(test.Name, uint32(test.Value.(uint64)))
176 case registry.QWORD:
177 err = k.SetQWordValue(test.Name, test.Value.(uint64))
178 default:
179 t.Fatalf("unsupported type %d for %s value", test.Type, test.Name)
180 }
181 if test.WillFail {
182 if err == nil {
183 t.Fatalf("setting %s value %q should fail, but succeeded", test.Name, test.Value)
184 }
185 } else {
186 if err != nil {
187 t.Fatal(err)
188 }
189 }
190 }
191 }
192
193 func enumerateValues(t *testing.T, k registry.Key) {
194 names, err := k.ReadValueNames(-1)
195 if err != nil {
196 t.Error(err)
197 return
198 }
199 haveNames := make(map[string]bool)
200 for _, n := range names {
201 haveNames[n] = false
202 }
203 for _, test := range ValueTests {
204 wantFound := !test.WillFail
205 _, haveFound := haveNames[test.Name]
206 if wantFound && !haveFound {
207 t.Errorf("value %s is not found while enumerating", test.Name)
208 }
209 if haveFound && !wantFound {
210 t.Errorf("value %s is found while enumerating, but expected to fail", test.Name)
211 }
212 if haveFound {
213 delete(haveNames, test.Name)
214 }
215 }
216 for n, v := range haveNames {
217 t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v)
218 }
219 }
220
221 func testErrNotExist(t *testing.T, name string, err error) {
222 if err == nil {
223 t.Errorf("%s value should not exist", name)
224 return
225 }
226 if err != registry.ErrNotExist {
227 t.Errorf("reading %s value should return 'not exist' error, but got: %s", name, err)
228 return
229 }
230 }
231
232 func testErrUnexpectedType(t *testing.T, test ValueTest, gottype uint32, err error) {
233 if err == nil {
234 t.Errorf("GetXValue(%q) should not succeed", test.Name)
235 return
236 }
237 if err != registry.ErrUnexpectedType {
238 t.Errorf("reading %s value should return 'unexpected key value type' error, but got: %s", test.Name, err)
239 return
240 }
241 if gottype != test.Type {
242 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
243 return
244 }
245 }
246
247 func testGetStringValue(t *testing.T, k registry.Key, test ValueTest) {
248 got, gottype, err := k.GetStringValue(test.Name)
249 if err != nil {
250 t.Errorf("GetStringValue(%s) failed: %v", test.Name, err)
251 return
252 }
253 if got != test.Value {
254 t.Errorf("want %s value %q, got %q", test.Name, test.Value, got)
255 return
256 }
257 if gottype != test.Type {
258 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
259 return
260 }
261 if gottype == registry.EXPAND_SZ {
262 _, err = registry.ExpandString(got)
263 if err != nil {
264 t.Errorf("ExpandString(%s) failed: %v", got, err)
265 return
266 }
267 }
268 }
269
270 func testGetIntegerValue(t *testing.T, k registry.Key, test ValueTest) {
271 got, gottype, err := k.GetIntegerValue(test.Name)
272 if err != nil {
273 t.Errorf("GetIntegerValue(%s) failed: %v", test.Name, err)
274 return
275 }
276 if got != test.Value.(uint64) {
277 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
278 return
279 }
280 if gottype != test.Type {
281 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
282 return
283 }
284 }
285
286 func testGetBinaryValue(t *testing.T, k registry.Key, test ValueTest) {
287 got, gottype, err := k.GetBinaryValue(test.Name)
288 if err != nil {
289 t.Errorf("GetBinaryValue(%s) failed: %v", test.Name, err)
290 return
291 }
292 if !bytes.Equal(got, test.Value.([]byte)) {
293 t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
294 return
295 }
296 if gottype != test.Type {
297 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
298 return
299 }
300 }
301
302 func testGetStringsValue(t *testing.T, k registry.Key, test ValueTest) {
303 got, gottype, err := k.GetStringsValue(test.Name)
304 if err != nil {
305 t.Errorf("GetStringsValue(%s) failed: %v", test.Name, err)
306 return
307 }
308 if !equalStringSlice(got, test.Value.([]string)) {
309 t.Errorf("want %s value %#v, got %#v", test.Name, test.Value, got)
310 return
311 }
312 if gottype != test.Type {
313 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
314 return
315 }
316 }
317
318 func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) {
319 if size <= 0 {
320 return
321 }
322
323 gotsize, gottype, err := k.GetValue(test.Name, nil)
324 if err != nil {
325 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
326 return
327 }
328 if gotsize != size {
329 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
330 return
331 }
332 if gottype != test.Type {
333 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
334 return
335 }
336
337 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
338 if err == nil {
339 t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1)
340 return
341 }
342 if err != registry.ErrShortBuffer {
343 t.Errorf("reading %s value should return 'short buffer' error, but got: %s", test.Name, err)
344 return
345 }
346 if gotsize != size {
347 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
348 return
349 }
350 if gottype != test.Type {
351 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
352 return
353 }
354
355 gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size))
356 if err != nil {
357 t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
358 return
359 }
360 if gotsize != size {
361 t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
362 return
363 }
364 if gottype != test.Type {
365 t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
366 return
367 }
368
369 _, _, err = k.GetValue(test.Name+"_not_there", make([]byte, size))
370 if err == nil {
371 t.Errorf("GetValue(%q) should not succeed", test.Name)
372 return
373 }
374 if err != registry.ErrNotExist {
375 t.Errorf("GetValue(%q) should return 'not exist' error, but got: %s", test.Name, err)
376 return
377 }
378 }
379
380 func testValues(t *testing.T, k registry.Key) {
381 for _, test := range ValueTests {
382 switch test.Type {
383 case registry.SZ, registry.EXPAND_SZ:
384 if test.WillFail {
385 _, _, err := k.GetStringValue(test.Name)
386 testErrNotExist(t, test.Name, err)
387 } else {
388 testGetStringValue(t, k, test)
389 _, gottype, err := k.GetIntegerValue(test.Name)
390 testErrUnexpectedType(t, test, gottype, err)
391
392
393
394 testGetValue(t, k, test, (len(test.Value.(string))+1)*2)
395 }
396 _, _, err := k.GetStringValue(test.Name + "_string_not_created")
397 testErrNotExist(t, test.Name+"_string_not_created", err)
398 case registry.DWORD, registry.QWORD:
399 testGetIntegerValue(t, k, test)
400 _, gottype, err := k.GetBinaryValue(test.Name)
401 testErrUnexpectedType(t, test, gottype, err)
402 _, _, err = k.GetIntegerValue(test.Name + "_int_not_created")
403 testErrNotExist(t, test.Name+"_int_not_created", err)
404 size := 8
405 if test.Type == registry.DWORD {
406 size = 4
407 }
408 testGetValue(t, k, test, size)
409 case registry.BINARY:
410 testGetBinaryValue(t, k, test)
411 _, gottype, err := k.GetStringsValue(test.Name)
412 testErrUnexpectedType(t, test, gottype, err)
413 _, _, err = k.GetBinaryValue(test.Name + "_byte_not_created")
414 testErrNotExist(t, test.Name+"_byte_not_created", err)
415 testGetValue(t, k, test, len(test.Value.([]byte)))
416 case registry.MULTI_SZ:
417 if test.WillFail {
418 _, _, err := k.GetStringsValue(test.Name)
419 testErrNotExist(t, test.Name, err)
420 } else {
421 testGetStringsValue(t, k, test)
422 _, gottype, err := k.GetStringValue(test.Name)
423 testErrUnexpectedType(t, test, gottype, err)
424 size := 0
425 for _, s := range test.Value.([]string) {
426 size += len(s) + 1
427 }
428 size += 1
429 size *= 2
430 testGetValue(t, k, test, size)
431 }
432 _, _, err := k.GetStringsValue(test.Name + "_strings_not_created")
433 testErrNotExist(t, test.Name+"_strings_not_created", err)
434 default:
435 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
436 continue
437 }
438 }
439 }
440
441 func testStat(t *testing.T, k registry.Key) {
442 subk, _, err := registry.CreateKey(k, "subkey", registry.CREATE_SUB_KEY)
443 if err != nil {
444 t.Error(err)
445 return
446 }
447 defer subk.Close()
448
449 defer registry.DeleteKey(k, "subkey")
450
451 ki, err := k.Stat()
452 if err != nil {
453 t.Error(err)
454 return
455 }
456 if ki.SubKeyCount != 1 {
457 t.Error("key must have 1 subkey")
458 }
459 if ki.MaxSubKeyLen != 6 {
460 t.Error("key max subkey name length must be 6")
461 }
462 if ki.ValueCount != 24 {
463 t.Errorf("key must have 24 values, but is %d", ki.ValueCount)
464 }
465 if ki.MaxValueNameLen != 12 {
466 t.Errorf("key max value name length must be 10, but is %d", ki.MaxValueNameLen)
467 }
468 if ki.MaxValueLen != 38 {
469 t.Errorf("key max value length must be 38, but is %d", ki.MaxValueLen)
470 }
471 if mt, ct := ki.ModTime(), time.Now(); ct.Sub(mt) > 100*time.Millisecond {
472 t.Errorf("key mod time is not close to current time: mtime=%v current=%v delta=%v", mt, ct, ct.Sub(mt))
473 }
474 }
475
476 func deleteValues(t *testing.T, k registry.Key) {
477 for _, test := range ValueTests {
478 if test.WillFail {
479 continue
480 }
481 err := k.DeleteValue(test.Name)
482 if err != nil {
483 t.Error(err)
484 continue
485 }
486 }
487 names, err := k.ReadValueNames(-1)
488 if err != nil {
489 t.Error(err)
490 return
491 }
492 if len(names) != 0 {
493 t.Errorf("some values remain after deletion: %v", names)
494 }
495 }
496
497 func TestValues(t *testing.T) {
498 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
499 if err != nil {
500 t.Fatal(err)
501 }
502 defer softwareK.Close()
503
504 testKName := randKeyName("TestValues_")
505
506 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
507 if err != nil {
508 t.Fatal(err)
509 }
510 defer k.Close()
511
512 if exist {
513 t.Fatalf("key %q already exists", testKName)
514 }
515
516 defer registry.DeleteKey(softwareK, testKName)
517
518 setValues(t, k)
519
520 enumerateValues(t, k)
521
522 testValues(t, k)
523
524 testStat(t, k)
525
526 deleteValues(t, k)
527 }
528
529 func TestExpandString(t *testing.T) {
530 got, err := registry.ExpandString("%PATH%")
531 if err != nil {
532 t.Fatal(err)
533 }
534 want := os.Getenv("PATH")
535 if got != want {
536 t.Errorf("want %q string expanded, got %q", want, got)
537 }
538 }
539
540 func TestInvalidValues(t *testing.T) {
541 softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
542 if err != nil {
543 t.Fatal(err)
544 }
545 defer softwareK.Close()
546
547 testKName := randKeyName("TestInvalidValues_")
548
549 k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
550 if err != nil {
551 t.Fatal(err)
552 }
553 defer k.Close()
554
555 if exist {
556 t.Fatalf("key %q already exists", testKName)
557 }
558
559 defer registry.DeleteKey(softwareK, testKName)
560
561 var tests = []struct {
562 Type uint32
563 Name string
564 Data []byte
565 }{
566 {registry.DWORD, "Dword1", nil},
567 {registry.DWORD, "Dword2", []byte{1, 2, 3}},
568 {registry.QWORD, "Qword1", nil},
569 {registry.QWORD, "Qword2", []byte{1, 2, 3}},
570 {registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
571 {registry.MULTI_SZ, "MultiString1", nil},
572 {registry.MULTI_SZ, "MultiString2", []byte{0}},
573 {registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
574 {registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
575 {registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
576 }
577
578 for _, test := range tests {
579 err := k.SetValue(test.Name, test.Type, test.Data)
580 if err != nil {
581 t.Fatalf("SetValue for %q failed: %v", test.Name, err)
582 }
583 }
584
585 for _, test := range tests {
586 switch test.Type {
587 case registry.DWORD, registry.QWORD:
588 value, valType, err := k.GetIntegerValue(test.Name)
589 if err == nil {
590 t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
591 }
592 case registry.MULTI_SZ:
593 value, valType, err := k.GetStringsValue(test.Name)
594 if err == nil {
595 if len(value) != 0 {
596 t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
597 }
598 }
599 default:
600 t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
601 }
602 }
603 }
604
605 func TestGetMUIStringValue(t *testing.T) {
606 if err := registry.LoadRegLoadMUIString(); err != nil {
607 t.Skip("regLoadMUIString not supported; skipping")
608 }
609 if err := procGetDynamicTimeZoneInformation.Find(); err != nil {
610 t.Skipf("%s not supported; skipping", procGetDynamicTimeZoneInformation.Name)
611 }
612 var dtzi DynamicTimezoneinformation
613 if _, err := GetDynamicTimeZoneInformation(&dtzi); err != nil {
614 t.Fatal(err)
615 }
616 tzKeyName := syscall.UTF16ToString(dtzi.TimeZoneKeyName[:])
617 timezoneK, err := registry.OpenKey(registry.LOCAL_MACHINE,
618 `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+tzKeyName, registry.READ)
619 if err != nil {
620 t.Fatal(err)
621 }
622 defer timezoneK.Close()
623
624 type testType struct {
625 name string
626 want string
627 }
628 var tests = []testType{
629 {"MUI_Std", syscall.UTF16ToString(dtzi.StandardName[:])},
630 }
631 if dtzi.DynamicDaylightTimeDisabled == 0 {
632 tests = append(tests, testType{"MUI_Dlt", syscall.UTF16ToString(dtzi.DaylightName[:])})
633 }
634
635 for _, test := range tests {
636 got, err := timezoneK.GetMUIStringValue(test.name)
637 if err != nil {
638 t.Error("GetMUIStringValue:", err)
639 }
640
641 if got != test.want {
642 t.Errorf("GetMUIStringValue: %s: Got %q, want %q", test.name, got, test.want)
643 }
644 }
645 }
646
647 type DynamicTimezoneinformation struct {
648 Bias int32
649 StandardName [32]uint16
650 StandardDate syscall.Systemtime
651 StandardBias int32
652 DaylightName [32]uint16
653 DaylightDate syscall.Systemtime
654 DaylightBias int32
655 TimeZoneKeyName [128]uint16
656 DynamicDaylightTimeDisabled uint8
657 }
658
659 var (
660 kernel32DLL = syscall.NewLazyDLL("kernel32")
661
662 procGetDynamicTimeZoneInformation = kernel32DLL.NewProc("GetDynamicTimeZoneInformation")
663 )
664
665 func GetDynamicTimeZoneInformation(dtzi *DynamicTimezoneinformation) (rc uint32, err error) {
666 r0, _, e1 := syscall.Syscall(procGetDynamicTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(dtzi)), 0, 0)
667 rc = uint32(r0)
668 if rc == 0xffffffff {
669 if e1 != 0 {
670 err = error(e1)
671 } else {
672 err = syscall.EINVAL
673 }
674 }
675 return
676 }
677
View as plain text