<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Binary syscall_test_runner runs the syscall test suites in gVisor
// containers and on the host platform.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"testing"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"golang.org/x/sys/unix"
	"gvisor.googlesource.com/gvisor/pkg/log"
	"gvisor.googlesource.com/gvisor/runsc/specutils"
	"gvisor.googlesource.com/gvisor/runsc/test/testutil"
	"gvisor.googlesource.com/gvisor/test/syscalls/gtest"
)

// Location of syscall tests, relative to the repo root.
const testDir = "test/syscalls/linux"

var (
	testName  = flag.String("test-name", "", "name of test binary to run")
	debug     = flag.Bool("debug", false, "enable debug logs")
	strace    = flag.Bool("strace", false, "enable strace logs")
	platform  = flag.String("platform", "ptrace", "platform to run on")
	useTmpfs  = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
	parallel  = flag.Bool("parallel", false, "run tests in parallel")
	runscPath = flag.String("runsc", "", "path to runsc binary")
)

// runTestCaseNative runs the test case directly on the host machine.
func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
	// These tests might be running in parallel, so make sure they have a
	// unique test temp dir.
	tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
	if err != nil {
		t.Fatalf("could not create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	// Replace TEST_TMPDIR in the current environment with something
	// unique.
	env := os.Environ()
	newEnvVar := "TEST_TMPDIR=" + tmpDir
	var found bool
	for i, kv := range env {
		if strings.HasPrefix(kv, "TEST_TMPDIR=") {
			env[i] = newEnvVar
			found = true
			break
		}
	}
	if !found {
		env = append(env, newEnvVar)
	}
	// Remove env variables that cause the gunit binary to write output
	// files, since they will stomp on eachother, and on the output files
	// from this go test.
	env = filterEnv(env, []string{"GUNIT_OUTPUT", "TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"})

	// Remove shard env variables so that the gunit binary does not try to
	// intepret them.
	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS"})

	cmd := exec.Command(testBin, gtest.FilterTestFlag+"="+tc.FullName())
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus)
		t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus())
	}
}

// runsTestCaseRunsc runs the test case in runsc.
func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
	rootDir, err := testutil.SetupRootDir()
	if err != nil {
		t.Fatalf("SetupRootDir failed: %v", err)
	}
	defer os.RemoveAll(rootDir)

	// Run a new container with the test executable and filter for the
	// given test suite and name.
	spec := testutil.NewSpecWithArgs(testBin, gtest.FilterTestFlag+"="+tc.FullName())

	// Mark the root as writeable, as some tests attempt to
	// write to the rootfs, and expect EACCES, not EROFS.
	spec.Root.Readonly = false

	// Test spec comes with pre-defined mounts that we don't want. Reset it.
	spec.Mounts = nil
	if *useTmpfs {
		// Forces '/tmp' to be mounted as tmpfs, otherwise test that rely on
		// features available in gVisor's tmpfs and not gofers, may fail.
		spec.Mounts = []specs.Mount{
			{Destination: "/tmp", Type: "tmpfs"},
		}
	}

	// Set environment variable that indicates we are
	// running in gVisor and with the given platform.
	platformVar := "TEST_ON_GVISOR"
	env := append(os.Environ(), platformVar+"="+*platform)

	// Remove env variables that cause the gunit binary to write output
	// files, since they will stomp on eachother, and on the output files
	// from this go test.
	env = filterEnv(env, []string{"GUNIT_OUTPUT", "TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"})

	// Remove shard env variables so that the gunit binary does not try to
	// intepret them.
	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS"})

	// Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to
	// be backed by tmpfs.
	for i, kv := range env {
		if strings.HasPrefix(kv, "TEST_TMPDIR=") {
			env[i] = "TEST_TMPDIR=/tmp"
			break
		}
	}

	spec.Process.Env = env

	bundleDir, err := testutil.SetupBundleDir(spec)
	if err != nil {
		t.Fatalf("SetupBundleDir failed: %v", err)
	}
	defer os.RemoveAll(bundleDir)

	id := testutil.UniqueContainerID()
	log.Infof("Running test %q in container %q", tc.FullName(), id)
	specutils.LogSpec(spec)

	args := []string{
		"-platform", *platform,
		"-root", rootDir,
		"--network=none",
		"-log-format=text",
		"-TESTONLY-unsafe-nonroot=true",
	}
	if *debug {
		args = append(args, "-debug", "-log-packets=true")
	}
	if *strace {
		args = append(args, "-strace")
	}
	// Current process doesn't have CAP_SYS_ADMIN, create user namespace and run
	// as root inside that namespace to get it.
	args = append(args, "run", "--bundle", bundleDir, id)
	cmd := exec.Command(*runscPath, args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS,
		// Set current user/group as root inside the namespace.
		UidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getuid(), Size: 1},
		},
		GidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getgid(), Size: 1},
		},
		GidMappingsEnableSetgroups: false,
		Credential: &syscall.Credential{
			Uid: 0,
			Gid: 0,
		},
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		t.Errorf("test %q exited with status %v, want 0", tc.FullName(), err)
	}
}

// filterEnv returns an environment with the blacklisted variables removed.
func filterEnv(env, blacklist []string) []string {
	var out []string
	for _, kv := range env {
		ok := true
		for _, k := range blacklist {
			if strings.HasPrefix(kv, k+"=") {
				ok = false
				break
			}
		}
		if ok {
			out = append(out, kv)
		}
	}
	return out
}

func fatalf(s string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, s+"\n", args...)
	os.Exit(1)
}

func matchString(a, b string) (bool, error) {
	return a == b, nil
}

func main() {
	flag.Parse()
	if *testName == "" {
		fatalf("test-name flag must be provided")
	}

	log.SetLevel(log.Warning)
	if *debug {
		log.SetLevel(log.Debug)
	}

	if *platform != "native" && *runscPath == "" {
		if err := testutil.ConfigureExePath(); err != nil {
			panic(err.Error())
		}
		*runscPath = specutils.ExePath
	}

	// Make sure stdout and stderr are opened with O_APPEND, otherwise logs
	// from outside the sandbox can (and will) stomp on logs from inside
	// the sandbox.
	for _, f := range []*os.File{os.Stdout, os.Stderr} {
		flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
		if err != nil {
			fatalf("error getting file flags for %v: %v", f, err)
		}
		if flags&unix.O_APPEND == 0 {
			flags |= unix.O_APPEND
			if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
				fatalf("error setting file flags for %v: %v", f, err)
			}
		}
	}

	// Get path to test binary.
	fullTestName := filepath.Join(testDir, *testName)
	testBin, err := testutil.FindFile(fullTestName)
	if err != nil {
		fatalf("FindFile(%q) failed: %v", fullTestName, err)
	}

	// Get all test cases in each binary.
	testCases, err := gtest.ParseTestCases(testBin)
	if err != nil {
		fatalf("ParseTestCases(%q) failed: %v", testBin, err)
	}

	// If sharding, then get the subset of tests to run based on the shard index.
	if indexStr, totalStr := os.Getenv("TEST_SHARD_INDEX"), os.Getenv("TEST_TOTAL_SHARDS"); indexStr != "" && totalStr != "" {
		// Parse index and total to ints.
		index, err := strconv.Atoi(indexStr)
		if err != nil {
			fatalf("invalid TEST_SHARD_INDEX %q: %v", indexStr, err)
		}
		total, err := strconv.Atoi(totalStr)
		if err != nil {
			fatalf("invalid TEST_TOTAL_SHARDS %q: %v", totalStr, err)
		}
		// Calculate subslice of tests to run.
		shardSize := int(math.Ceil(float64(len(testCases)) / float64(total)))
		begin := index * shardSize
		// Set end as begin of next subslice.
		end := ((index + 1) * shardSize)
		if begin > len(testCases) {
			// Nothing to run.
			return
		}
		if end > len(testCases) {
			end = len(testCases)
		}
		testCases = testCases[begin:end]
	}

	var tests []testing.InternalTest
	for _, tc := range testCases {
		// Capture tc.
		tc := tc
		testName := fmt.Sprintf("%s_%s", tc.Suite, tc.Name)
		tests = append(tests, testing.InternalTest{
			Name: testName,
			F: func(t *testing.T) {
				if *parallel {
					t.Parallel()
				}
				if *platform == "native" {
					// Run the test case on host.
					runTestCaseNative(testBin, tc, t)
				} else {
					// Run the test case in runsc.
					runTestCaseRunsc(testBin, tc, t)
				}
			},
		})
	}

	testing.Main(matchString, tests, nil, nil)
}
<commit_msg>Add runsc logs to test results<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Binary syscall_test_runner runs the syscall test suites in gVisor
// containers and on the host platform.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"testing"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"golang.org/x/sys/unix"
	"gvisor.googlesource.com/gvisor/pkg/log"
	"gvisor.googlesource.com/gvisor/runsc/specutils"
	"gvisor.googlesource.com/gvisor/runsc/test/testutil"
	"gvisor.googlesource.com/gvisor/test/syscalls/gtest"
)

// Location of syscall tests, relative to the repo root.
const testDir = "test/syscalls/linux"

var (
	testName  = flag.String("test-name", "", "name of test binary to run")
	debug     = flag.Bool("debug", false, "enable debug logs")
	strace    = flag.Bool("strace", false, "enable strace logs")
	platform  = flag.String("platform", "ptrace", "platform to run on")
	useTmpfs  = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
	parallel  = flag.Bool("parallel", false, "run tests in parallel")
	runscPath = flag.String("runsc", "", "path to runsc binary")
)

// runTestCaseNative runs the test case directly on the host machine.
func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
	// These tests might be running in parallel, so make sure they have a
	// unique test temp dir.
	tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
	if err != nil {
		t.Fatalf("could not create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	// Replace TEST_TMPDIR in the current environment with something
	// unique.
	env := os.Environ()
	newEnvVar := "TEST_TMPDIR=" + tmpDir
	var found bool
	for i, kv := range env {
		if strings.HasPrefix(kv, "TEST_TMPDIR=") {
			env[i] = newEnvVar
			found = true
			break
		}
	}
	if !found {
		env = append(env, newEnvVar)
	}
	// Remove env variables that cause the gunit binary to write output
	// files, since they will stomp on eachother, and on the output files
	// from this go test.
	env = filterEnv(env, []string{"GUNIT_OUTPUT", "TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"})

	// Remove shard env variables so that the gunit binary does not try to
	// intepret them.
	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS"})

	cmd := exec.Command(testBin, gtest.FilterTestFlag+"="+tc.FullName())
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus)
		t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus())
	}
}

// runsTestCaseRunsc runs the test case in runsc.
func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
	rootDir, err := testutil.SetupRootDir()
	if err != nil {
		t.Fatalf("SetupRootDir failed: %v", err)
	}
	defer os.RemoveAll(rootDir)

	// Run a new container with the test executable and filter for the
	// given test suite and name.
	spec := testutil.NewSpecWithArgs(testBin, gtest.FilterTestFlag+"="+tc.FullName())

	// Mark the root as writeable, as some tests attempt to
	// write to the rootfs, and expect EACCES, not EROFS.
	spec.Root.Readonly = false

	// Test spec comes with pre-defined mounts that we don't want. Reset it.
	spec.Mounts = nil
	if *useTmpfs {
		// Forces '/tmp' to be mounted as tmpfs, otherwise test that rely on
		// features available in gVisor's tmpfs and not gofers, may fail.
		spec.Mounts = []specs.Mount{
			{Destination: "/tmp", Type: "tmpfs"},
		}
	}

	// Set environment variable that indicates we are
	// running in gVisor and with the given platform.
	platformVar := "TEST_ON_GVISOR"
	env := append(os.Environ(), platformVar+"="+*platform)

	// Remove env variables that cause the gunit binary to write output
	// files, since they will stomp on eachother, and on the output files
	// from this go test.
	env = filterEnv(env, []string{"GUNIT_OUTPUT", "TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"})

	// Remove shard env variables so that the gunit binary does not try to
	// intepret them.
	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS"})

	// Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to
	// be backed by tmpfs.
	for i, kv := range env {
		if strings.HasPrefix(kv, "TEST_TMPDIR=") {
			env[i] = "TEST_TMPDIR=/tmp"
			break
		}
	}

	spec.Process.Env = env

	bundleDir, err := testutil.SetupBundleDir(spec)
	if err != nil {
		t.Fatalf("SetupBundleDir failed: %v", err)
	}
	defer os.RemoveAll(bundleDir)

	id := testutil.UniqueContainerID()
	log.Infof("Running test %q in container %q", tc.FullName(), id)
	specutils.LogSpec(spec)

	args := []string{
		"-platform", *platform,
		"-root", rootDir,
		"--network=none",
		"-log-format=text",
		"-TESTONLY-unsafe-nonroot=true",
	}
	if *debug {
		args = append(args, "-debug", "-log-packets=true")
	}
	if *strace {
		args = append(args, "-strace")
	}
	if outDir, ok := syscall.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); ok {
		args = append(args, "-debug-log", outDir+"/")
	}

	// Current process doesn't have CAP_SYS_ADMIN, create user namespace and run
	// as root inside that namespace to get it.
	args = append(args, "run", "--bundle", bundleDir, id)
	cmd := exec.Command(*runscPath, args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS,
		// Set current user/group as root inside the namespace.
		UidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getuid(), Size: 1},
		},
		GidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: os.Getgid(), Size: 1},
		},
		GidMappingsEnableSetgroups: false,
		Credential: &syscall.Credential{
			Uid: 0,
			Gid: 0,
		},
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		t.Errorf("test %q exited with status %v, want 0", tc.FullName(), err)
	}
}

// filterEnv returns an environment with the blacklisted variables removed.
func filterEnv(env, blacklist []string) []string {
	var out []string
	for _, kv := range env {
		ok := true
		for _, k := range blacklist {
			if strings.HasPrefix(kv, k+"=") {
				ok = false
				break
			}
		}
		if ok {
			out = append(out, kv)
		}
	}
	return out
}

func fatalf(s string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, s+"\n", args...)
	os.Exit(1)
}

func matchString(a, b string) (bool, error) {
	return a == b, nil
}

func main() {
	flag.Parse()
	if *testName == "" {
		fatalf("test-name flag must be provided")
	}

	log.SetLevel(log.Warning)
	if *debug {
		log.SetLevel(log.Debug)
	}

	if *platform != "native" && *runscPath == "" {
		if err := testutil.ConfigureExePath(); err != nil {
			panic(err.Error())
		}
		*runscPath = specutils.ExePath
	}

	// Make sure stdout and stderr are opened with O_APPEND, otherwise logs
	// from outside the sandbox can (and will) stomp on logs from inside
	// the sandbox.
	for _, f := range []*os.File{os.Stdout, os.Stderr} {
		flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
		if err != nil {
			fatalf("error getting file flags for %v: %v", f, err)
		}
		if flags&unix.O_APPEND == 0 {
			flags |= unix.O_APPEND
			if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
				fatalf("error setting file flags for %v: %v", f, err)
			}
		}
	}

	// Get path to test binary.
	fullTestName := filepath.Join(testDir, *testName)
	testBin, err := testutil.FindFile(fullTestName)
	if err != nil {
		fatalf("FindFile(%q) failed: %v", fullTestName, err)
	}

	// Get all test cases in each binary.
	testCases, err := gtest.ParseTestCases(testBin)
	if err != nil {
		fatalf("ParseTestCases(%q) failed: %v", testBin, err)
	}

	// If sharding, then get the subset of tests to run based on the shard index.
	if indexStr, totalStr := os.Getenv("TEST_SHARD_INDEX"), os.Getenv("TEST_TOTAL_SHARDS"); indexStr != "" && totalStr != "" {
		// Parse index and total to ints.
		index, err := strconv.Atoi(indexStr)
		if err != nil {
			fatalf("invalid TEST_SHARD_INDEX %q: %v", indexStr, err)
		}
		total, err := strconv.Atoi(totalStr)
		if err != nil {
			fatalf("invalid TEST_TOTAL_SHARDS %q: %v", totalStr, err)
		}
		// Calculate subslice of tests to run.
		shardSize := int(math.Ceil(float64(len(testCases)) / float64(total)))
		begin := index * shardSize
		// Set end as begin of next subslice.
		end := ((index + 1) * shardSize)
		if begin > len(testCases) {
			// Nothing to run.
			return
		}
		if end > len(testCases) {
			end = len(testCases)
		}
		testCases = testCases[begin:end]
	}

	var tests []testing.InternalTest
	for _, tc := range testCases {
		// Capture tc.
		tc := tc
		testName := fmt.Sprintf("%s_%s", tc.Suite, tc.Name)
		tests = append(tests, testing.InternalTest{
			Name: testName,
			F: func(t *testing.T) {
				if *parallel {
					t.Parallel()
				}
				if *platform == "native" {
					// Run the test case on host.
					runTestCaseNative(testBin, tc, t)
				} else {
					// Run the test case in runsc.
					runTestCaseRunsc(testBin, tc, t)
				}
			},
		})
	}

	testing.Main(matchString, tests, nil, nil)
}
<|endoftext|>


<commit_before>package irc

import (
	"crypto/sha1"
	"fmt"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// Register a callback to a connection and event code. A callback is a function
// which takes only an Event pointer as parameter. Valid event codes are all
// IRC/CTCP commands and error/response codes. This function returns the ID of
// the registered callback for later management.
func (irc *Connection) AddCallback(eventcode string, callback func(*Event)) string {
	eventcode = strings.ToUpper(eventcode)

	if _, ok := irc.events[eventcode]; !ok {
		irc.events[eventcode] = make(map[string]func(*Event))
	}
	h := sha1.New()
	rawId := []byte(fmt.Sprintf("%v%d", reflect.ValueOf(callback).Pointer(), rand.Int63()))
	h.Write(rawId)
	id := fmt.Sprintf("%x", h.Sum(nil))
	irc.events[eventcode][id] = callback
	return id
}

// Remove callback i (ID) from the given event code. This functions returns
// true upon success, false if any error occurs.
func (irc *Connection) RemoveCallback(eventcode string, i string) bool {
	eventcode = strings.ToUpper(eventcode)

	if event, ok := irc.events[eventcode]; ok {
		if _, ok := event[i]; ok {
			delete(irc.events[eventcode], i)
			return true
		}
		irc.Log.Printf("Event found, but no callback found at id %s\n", i)
		return false
	}

	irc.Log.Println("Event not found")
	return false
}

// Remove all callbacks from a given event code. It returns true
// if given event code is found and cleared.
func (irc *Connection) ClearCallback(eventcode string) bool {
	eventcode = strings.ToUpper(eventcode)

	if _, ok := irc.events[eventcode]; ok {
		irc.events[eventcode] = make(map[string]func(*Event))
		return true
	}

	irc.Log.Println("Event not found")
	return false
}

// Replace callback i (ID) associated with a given event code with a new callback function.
func (irc *Connection) ReplaceCallback(eventcode string, i string, callback func(*Event)) {
	eventcode = strings.ToUpper(eventcode)

	if event, ok := irc.events[eventcode]; ok {
		if _, ok := event[i]; ok {
			event[i] = callback
			return
		}
		irc.Log.Printf("Event found, but no callback found at id %s\n", i)
	}
	irc.Log.Printf("Event not found. Use AddCallBack\n")
}

// Execute all callbacks associated with a given event.
func (irc *Connection) RunCallbacks(event *Event) {
	msg := event.Message()
	if event.Code == "PRIVMSG" && len(msg) > 0 && msg[0] == '\x01' {
		event.Code = "CTCP" //Unknown CTCP

		if i := strings.LastIndex(msg, "\x01"); i > -1 {
			msg = msg[1:i]
		}

		if msg == "VERSION" {
			event.Code = "CTCP_VERSION"

		} else if msg == "TIME" {
			event.Code = "CTCP_TIME"

		} else if msg[0:4] == "PING" {
			event.Code = "CTCP_PING"

		} else if msg == "USERINFO" {
			event.Code = "CTCP_USERINFO"

		} else if msg == "CLIENTINFO" {
			event.Code = "CTCP_CLIENTINFO"

		} else if msg[0:6] == "ACTION" {
			event.Code = "CTCP_ACTION"
			msg = msg[7:]
		}
		event.Arguments[len(event.Arguments)-1] = msg
	}

	if callbacks, ok := irc.events[event.Code]; ok {
		if irc.VerboseCallbackHandler {
			irc.Log.Printf("%v (%v) >> %#v\n", event.Code, len(callbacks), event)
		}

		for _, callback := range callbacks {
			go callback(event)
		}
	} else if irc.VerboseCallbackHandler {
		irc.Log.Printf("%v (0) >> %#v\n", event.Code, event)
	}

	if callbacks, ok := irc.events["*"]; ok {
		if irc.VerboseCallbackHandler {
			irc.Log.Printf("Wildcard %v (%v) >> %#v\n", event.Code, len(callbacks), event)
		}

		for _, callback := range callbacks {
			go callback(event)
		}
	}
}

// Set up some initial callbacks to handle the IRC/CTCP protocol.
func (irc *Connection) setupCallbacks() {
	irc.events = make(map[string]map[string]func(*Event))

	//Handle ping events
	irc.AddCallback("PING", func(e *Event) { irc.SendRaw("PONG :" + e.Message()) })

	//Version handler
	irc.AddCallback("CTCP_VERSION", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01VERSION %s\x01", e.Nick, irc.Version)
	})

	irc.AddCallback("CTCP_USERINFO", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01USERINFO %s\x01", e.Nick, irc.user)
	})

	irc.AddCallback("CTCP_CLIENTINFO", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick)
	})

	irc.AddCallback("CTCP_TIME", func(e *Event) {
		ltime := time.Now()
		irc.SendRawf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String())
	})

	irc.AddCallback("CTCP_PING", func(e *Event) { irc.SendRawf("NOTICE %s :\x01%s\x01", e.Nick, e.Message ()) })

	irc.AddCallback("437", func(e *Event) {
		irc.nickcurrent = irc.nickcurrent + "_"
		irc.SendRawf("NICK %s", irc.nickcurrent)
	})

	irc.AddCallback("433", func(e *Event) {
		if len(irc.nickcurrent) > 8 {
			irc.nickcurrent = "_" + irc.nickcurrent

		} else {
			irc.nickcurrent = irc.nickcurrent + "_"
		}
		irc.SendRawf("NICK %s", irc.nickcurrent)
	})

	irc.AddCallback("PONG", func(e *Event) {
		ns, _ := strconv.ParseInt(e.Message(), 10, 64)
		delta := time.Duration(time.Now().UnixNano() - ns)
		if irc.Debug {
			irc.Log.Printf("Lag: %vs\n", delta)
		}
	})

	irc.AddCallback("NICK", func(e *Event) {
		if e.Nick == irc.nick {
			irc.nickcurrent = e.Message()
		}
	})

	irc.AddCallback("001", func(e *Event) {
		irc.nickcurrent = e.Arguments[0]
	})
}
<commit_msg>Add default callback for ERROR<commit_after>package irc

import (
	"crypto/sha1"
	"fmt"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// Register a callback to a connection and event code. A callback is a function
// which takes only an Event pointer as parameter. Valid event codes are all
// IRC/CTCP commands and error/response codes. This function returns the ID of
// the registered callback for later management.
func (irc *Connection) AddCallback(eventcode string, callback func(*Event)) string {
	eventcode = strings.ToUpper(eventcode)

	if _, ok := irc.events[eventcode]; !ok {
		irc.events[eventcode] = make(map[string]func(*Event))
	}
	h := sha1.New()
	rawId := []byte(fmt.Sprintf("%v%d", reflect.ValueOf(callback).Pointer(), rand.Int63()))
	h.Write(rawId)
	id := fmt.Sprintf("%x", h.Sum(nil))
	irc.events[eventcode][id] = callback
	return id
}

// Remove callback i (ID) from the given event code. This functions returns
// true upon success, false if any error occurs.
func (irc *Connection) RemoveCallback(eventcode string, i string) bool {
	eventcode = strings.ToUpper(eventcode)

	if event, ok := irc.events[eventcode]; ok {
		if _, ok := event[i]; ok {
			delete(irc.events[eventcode], i)
			return true
		}
		irc.Log.Printf("Event found, but no callback found at id %s\n", i)
		return false
	}

	irc.Log.Println("Event not found")
	return false
}

// Remove all callbacks from a given event code. It returns true
// if given event code is found and cleared.
func (irc *Connection) ClearCallback(eventcode string) bool {
	eventcode = strings.ToUpper(eventcode)

	if _, ok := irc.events[eventcode]; ok {
		irc.events[eventcode] = make(map[string]func(*Event))
		return true
	}

	irc.Log.Println("Event not found")
	return false
}

// Replace callback i (ID) associated with a given event code with a new callback function.
func (irc *Connection) ReplaceCallback(eventcode string, i string, callback func(*Event)) {
	eventcode = strings.ToUpper(eventcode)

	if event, ok := irc.events[eventcode]; ok {
		if _, ok := event[i]; ok {
			event[i] = callback
			return
		}
		irc.Log.Printf("Event found, but no callback found at id %s\n", i)
	}
	irc.Log.Printf("Event not found. Use AddCallBack\n")
}

// Execute all callbacks associated with a given event.
func (irc *Connection) RunCallbacks(event *Event) {
	msg := event.Message()
	if event.Code == "PRIVMSG" && len(msg) > 0 && msg[0] == '\x01' {
		event.Code = "CTCP" //Unknown CTCP

		if i := strings.LastIndex(msg, "\x01"); i > -1 {
			msg = msg[1:i]
		}

		if msg == "VERSION" {
			event.Code = "CTCP_VERSION"

		} else if msg == "TIME" {
			event.Code = "CTCP_TIME"

		} else if msg[0:4] == "PING" {
			event.Code = "CTCP_PING"

		} else if msg == "USERINFO" {
			event.Code = "CTCP_USERINFO"

		} else if msg == "CLIENTINFO" {
			event.Code = "CTCP_CLIENTINFO"

		} else if msg[0:6] == "ACTION" {
			event.Code = "CTCP_ACTION"
			msg = msg[7:]
		}
		event.Arguments[len(event.Arguments)-1] = msg
	}

	if callbacks, ok := irc.events[event.Code]; ok {
		if irc.VerboseCallbackHandler {
			irc.Log.Printf("%v (%v) >> %#v\n", event.Code, len(callbacks), event)
		}

		for _, callback := range callbacks {
			go callback(event)
		}
	} else if irc.VerboseCallbackHandler {
		irc.Log.Printf("%v (0) >> %#v\n", event.Code, event)
	}

	if callbacks, ok := irc.events["*"]; ok {
		if irc.VerboseCallbackHandler {
			irc.Log.Printf("Wildcard %v (%v) >> %#v\n", event.Code, len(callbacks), event)
		}

		for _, callback := range callbacks {
			go callback(event)
		}
	}
}

// Set up some initial callbacks to handle the IRC/CTCP protocol.
func (irc *Connection) setupCallbacks() {
	irc.events = make(map[string]map[string]func(*Event))

	//Handle error events
	irc.AddCallback("ERROR", func(e *Event) { irc.Disconnect() })

	//Handle ping events
	irc.AddCallback("PING", func(e *Event) { irc.SendRaw("PONG :" + e.Message()) })

	//Version handler
	irc.AddCallback("CTCP_VERSION", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01VERSION %s\x01", e.Nick, irc.Version)
	})

	irc.AddCallback("CTCP_USERINFO", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01USERINFO %s\x01", e.Nick, irc.user)
	})

	irc.AddCallback("CTCP_CLIENTINFO", func(e *Event) {
		irc.SendRawf("NOTICE %s :\x01CLIENTINFO PING VERSION TIME USERINFO CLIENTINFO\x01", e.Nick)
	})

	irc.AddCallback("CTCP_TIME", func(e *Event) {
		ltime := time.Now()
		irc.SendRawf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String())
	})

	irc.AddCallback("CTCP_PING", func(e *Event) { irc.SendRawf("NOTICE %s :\x01%s\x01", e.Nick, e.Message()) })

	irc.AddCallback("437", func(e *Event) {
		irc.nickcurrent = irc.nickcurrent + "_"
		irc.SendRawf("NICK %s", irc.nickcurrent)
	})

	irc.AddCallback("433", func(e *Event) {
		if len(irc.nickcurrent) > 8 {
			irc.nickcurrent = "_" + irc.nickcurrent

		} else {
			irc.nickcurrent = irc.nickcurrent + "_"
		}
		irc.SendRawf("NICK %s", irc.nickcurrent)
	})

	irc.AddCallback("PONG", func(e *Event) {
		ns, _ := strconv.ParseInt(e.Message(), 10, 64)
		delta := time.Duration(time.Now().UnixNano() - ns)
		if irc.Debug {
			irc.Log.Printf("Lag: %vs\n", delta)
		}
	})

	irc.AddCallback("NICK", func(e *Event) {
		if e.Nick == irc.nick {
			irc.nickcurrent = e.Message()
		}
	})

	irc.AddCallback("001", func(e *Event) {
		irc.nickcurrent = e.Arguments[0]
	})
}
<|endoftext|>


<commit_before>package commands

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/ipfs/go-ipfs/commands/files"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/repo/config"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)

type OptMap map[string]interface{}

type Context struct {
	Online     bool
	ConfigRoot string
	ReqLog     *ReqLog

	config     *config.Config
	LoadConfig func(path string) (*config.Config, error)

	node          *core.IpfsNode
	ConstructNode func() (*core.IpfsNode, error)
}

// GetConfig returns the config of the current Command exection
// context. It may load it with the providied function.
func (c *Context) GetConfig() (*config.Config, error) {
	var err error
	if c.config == nil {
		if c.LoadConfig == nil {
			return nil, errors.New("nil LoadConfig function")
		}
		c.config, err = c.LoadConfig(c.ConfigRoot)
	}
	return c.config, err
}

// GetNode returns the node of the current Command exection
// context. It may construct it with the provided function.
func (c *Context) GetNode() (*core.IpfsNode, error) {
	var err error
	if c.node == nil {
		if c.ConstructNode == nil {
			return nil, errors.New("nil ConstructNode function")
		}
		c.node, err = c.ConstructNode()
	}
	return c.node, err
}

// NodeWithoutConstructing returns the underlying node variable
// so that clients may close it.
func (c *Context) NodeWithoutConstructing() *core.IpfsNode {
	return c.node
}

// Request represents a call to a command from a consumer
type Request interface {
	Path() []string
	Option(name string) *OptionValue
	Options() OptMap
	SetOption(name string, val interface{})
	SetOptions(opts OptMap) error
	Arguments() []string
	SetArguments([]string)
	Files() files.File
	SetFiles(files.File)
	Context() context.Context
	SetRootContext(context.Context) error
	InvocContext() *Context
	SetInvocContext(Context)
	Command() *Command
	Values() map[string]interface{}
	Stdin() io.Reader
	VarArgs(func(string) error) error

	ConvertOptions() error
}

type request struct {
	path       []string
	options    OptMap
	arguments  []string
	files      files.File
	cmd        *Command
	ctx        Context
	rctx       context.Context
	optionDefs map[string]Option
	values     map[string]interface{}
	stdin      io.Reader
}

// Path returns the command path of this request
func (r *request) Path() []string {
	return r.path
}

// Option returns the value of the option for given name.
func (r *request) Option(name string) *OptionValue {
	// find the option with the specified name
	option, found := r.optionDefs[name]
	if !found {
		return nil
	}

	// try all the possible names, break if we find a value
	for _, n := range option.Names() {
		val, found := r.options[n]
		if found {
			return &OptionValue{val, found, option}
		}
	}

	return &OptionValue{option.DefaultVal(), false, option}
}

// Options returns a copy of the option map
func (r *request) Options() OptMap {
	output := make(OptMap)
	for k, v := range r.options {
		output[k] = v
	}
	return output
}

func (r *request) SetRootContext(ctx context.Context) error {
	ctx, err := getContext(ctx, r)
	if err != nil {
		return err
	}

	r.rctx = ctx
	return nil
}

// SetOption sets the value of the option for given name.
func (r *request) SetOption(name string, val interface{}) {
	// find the option with the specified name
	option, found := r.optionDefs[name]
	if !found {
		return
	}

	// try all the possible names, if we already have a value then set over it
	for _, n := range option.Names() {
		_, found := r.options[n]
		if found {
			r.options[n] = val
			return
		}
	}

	r.options[name] = val
}

// SetOptions sets the option values, unsetting any values that were previously set
func (r *request) SetOptions(opts OptMap) error {
	r.options = opts
	return r.ConvertOptions()
}

// Arguments returns the arguments slice
func (r *request) Arguments() []string {
	if r.haveVarArgsFromStdin() {
		err := r.VarArgs(func(s string) error {
			r.arguments = append(r.arguments, s)
			return nil
		})
		if err != nil && err != io.EOF {
			log.Error(err)
		}
	}

	return r.arguments
}

func (r *request) SetArguments(args []string) {
	r.arguments = args
}

func (r *request) Files() files.File {
	return r.files
}

func (r *request) SetFiles(f files.File) {
	r.files = f
}

func (r *request) Context() context.Context {
	return r.rctx
}

func (r *request) haveVarArgsFromStdin() bool {
	// we expect varargs if we have a variadic required argument and no arguments to
	// fill it
	if len(r.cmd.Arguments) == 0 {
		return false
	}

	last := r.cmd.Arguments[len(r.cmd.Arguments)-1]
	return last.SupportsStdin && last.Type == ArgString &&
		len(r.arguments) < len(r.cmd.Arguments)
}

func (r *request) VarArgs(f func(string) error) error {
	var i int
	for i = 0; i < len(r.cmd.Arguments); i++ {
		if r.cmd.Arguments[i].Variadic || r.cmd.Arguments[i].SupportsStdin {
			break
		}
	}

	args := r.arguments[i:]
	if len(args) > 0 {
		for _, arg := range args {
			err := f(arg)
			if err != nil {
				return err
			}
		}

		return nil
	} else {
		if r.files != nil {
			fi, err := r.files.NextFile()
			if err != nil {
				return err
			}

			if fi.FileName() == "*stdin*" {
				fmt.Fprintln(os.Stderr, "ipfs: Reading from stdin; send Ctrl-d to stop.")
			}

			scan := bufio.NewScanner(fi)
			for scan.Scan() {
				err := f(scan.Text())
				if err != nil {
					return err
				}
			}
			return nil
		} else {
			return fmt.Errorf("expected more arguments from stdin")
		}
	}
}

func getContext(base context.Context, req Request) (context.Context, error) {
	tout, found, err := req.Option("timeout").String()
	if err != nil {
		return nil, fmt.Errorf("error parsing timeout option: %s", err)
	}

	var ctx context.Context
	if found {
		duration, err := time.ParseDuration(tout)
		if err != nil {
			return nil, fmt.Errorf("error parsing timeout option: %s", err)
		}

		tctx, _ := context.WithTimeout(base, duration)
		ctx = tctx
	} else {
		cctx, _ := context.WithCancel(base)
		ctx = cctx
	}
	return ctx, nil
}

func (r *request) InvocContext() *Context {
	return &r.ctx
}

func (r *request) SetInvocContext(ctx Context) {
	r.ctx = ctx
}

func (r *request) Command() *Command {
	return r.cmd
}

type converter func(string) (interface{}, error)

var converters = map[reflect.Kind]converter{
	Bool: func(v string) (interface{}, error) {
		if v == "" {
			return true, nil
		}
		return strconv.ParseBool(v)
	},
	Int: func(v string) (interface{}, error) {
		val, err := strconv.ParseInt(v, 0, 32)
		if err != nil {
			return nil, err
		}
		return int(val), err
	},
	Uint: func(v string) (interface{}, error) {
		val, err := strconv.ParseUint(v, 0, 32)
		if err != nil {
			return nil, err
		}
		return int(val), err
	},
	Float: func(v string) (interface{}, error) {
		return strconv.ParseFloat(v, 64)
	},
}

func (r *request) Values() map[string]interface{} {
	return r.values
}

func (r *request) Stdin() io.Reader {
	return r.stdin
}

func (r *request) ConvertOptions() error {
	for k, v := range r.options {
		opt, ok := r.optionDefs[k]
		if !ok {
			continue
		}

		kind := reflect.TypeOf(v).Kind()
		if kind != opt.Type() {
			if kind == String {
				convert := converters[opt.Type()]
				str, ok := v.(string)
				if !ok {
					return u.ErrCast()
				}
				val, err := convert(str)
				if err != nil {
					value := fmt.Sprintf("value '%v'", v)
					if len(str) == 0 {
						value = "empty value"
					}
					return fmt.Errorf("Could not convert %s to type '%s' (for option '-%s')",
						value, opt.Type().String(), k)
				}
				r.options[k] = val

			} else {
				return fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
					k, opt.Type().String(), kind.String())
			}
		} else {
			r.options[k] = v
		}

		for _, name := range opt.Names() {
			if _, ok := r.options[name]; name != k && ok {
				return fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
					k, name)
			}
		}
	}

	return nil
}

// NewEmptyRequest initializes an empty request
func NewEmptyRequest() (Request, error) {
	return NewRequest(nil, nil, nil, nil, nil, nil)
}

// NewRequest returns a request initialized with given arguments
// An non-nil error will be returned if the provided option values are invalid
func NewRequest(path []string, opts OptMap, args []string, file files.File, cmd *Command, optDefs map[string]Option) (Request, error) {
	if opts == nil {
		opts = make(OptMap)
	}
	if optDefs == nil {
		optDefs = make(map[string]Option)
	}

	ctx := Context{}
	values := make(map[string]interface{})
	req := &request{
		path:       path,
		options:    opts,
		arguments:  args,
		files:      file,
		cmd:        cmd,
		ctx:        ctx,
		optionDefs: optDefs,
		values:     values,
		stdin:      os.Stdin,
	}
	err := req.ConvertOptions()
	if err != nil {
		return nil, err
	}

	return req, nil
}
<commit_msg>docs<commit_after>package commands

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/ipfs/go-ipfs/commands/files"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/repo/config"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)

type OptMap map[string]interface{}

type Context struct {
	Online     bool
	ConfigRoot string
	ReqLog     *ReqLog

	config     *config.Config
	LoadConfig func(path string) (*config.Config, error)

	node          *core.IpfsNode
	ConstructNode func() (*core.IpfsNode, error)
}

// GetConfig returns the config of the current Command exection
// context. It may load it with the providied function.
func (c *Context) GetConfig() (*config.Config, error) {
	var err error
	if c.config == nil {
		if c.LoadConfig == nil {
			return nil, errors.New("nil LoadConfig function")
		}
		c.config, err = c.LoadConfig(c.ConfigRoot)
	}
	return c.config, err
}

// GetNode returns the node of the current Command exection
// context. It may construct it with the provided function.
func (c *Context) GetNode() (*core.IpfsNode, error) {
	var err error
	if c.node == nil {
		if c.ConstructNode == nil {
			return nil, errors.New("nil ConstructNode function")
		}
		c.node, err = c.ConstructNode()
	}
	return c.node, err
}

// NodeWithoutConstructing returns the underlying node variable
// so that clients may close it.
func (c *Context) NodeWithoutConstructing() *core.IpfsNode {
	return c.node
}

// Request represents a call to a command from a consumer
type Request interface {
	Path() []string
	Option(name string) *OptionValue
	Options() OptMap
	SetOption(name string, val interface{})
	SetOptions(opts OptMap) error
	Arguments() []string
	SetArguments([]string)
	Files() files.File
	SetFiles(files.File)
	Context() context.Context
	SetRootContext(context.Context) error
	InvocContext() *Context
	SetInvocContext(Context)
	Command() *Command
	Values() map[string]interface{}
	Stdin() io.Reader
	VarArgs(func(string) error) error

	ConvertOptions() error
}

type request struct {
	path       []string
	options    OptMap
	arguments  []string
	files      files.File
	cmd        *Command
	ctx        Context
	rctx       context.Context
	optionDefs map[string]Option
	values     map[string]interface{}
	stdin      io.Reader
}

// Path returns the command path of this request
func (r *request) Path() []string {
	return r.path
}

// Option returns the value of the option for given name.
func (r *request) Option(name string) *OptionValue {
	// find the option with the specified name
	option, found := r.optionDefs[name]
	if !found {
		return nil
	}

	// try all the possible names, break if we find a value
	for _, n := range option.Names() {
		val, found := r.options[n]
		if found {
			return &OptionValue{val, found, option}
		}
	}

	return &OptionValue{option.DefaultVal(), false, option}
}

// Options returns a copy of the option map
func (r *request) Options() OptMap {
	output := make(OptMap)
	for k, v := range r.options {
		output[k] = v
	}
	return output
}

func (r *request) SetRootContext(ctx context.Context) error {
	ctx, err := getContext(ctx, r)
	if err != nil {
		return err
	}

	r.rctx = ctx
	return nil
}

// SetOption sets the value of the option for given name.
func (r *request) SetOption(name string, val interface{}) {
	// find the option with the specified name
	option, found := r.optionDefs[name]
	if !found {
		return
	}

	// try all the possible names, if we already have a value then set over it
	for _, n := range option.Names() {
		_, found := r.options[n]
		if found {
			r.options[n] = val
			return
		}
	}

	r.options[name] = val
}

// SetOptions sets the option values, unsetting any values that were previously set
func (r *request) SetOptions(opts OptMap) error {
	r.options = opts
	return r.ConvertOptions()
}

// Arguments returns the arguments slice
func (r *request) Arguments() []string {
	if r.haveVarArgsFromStdin() {
		err := r.VarArgs(func(s string) error {
			r.arguments = append(r.arguments, s)
			return nil
		})
		if err != nil && err != io.EOF {
			log.Error(err)
		}
	}

	return r.arguments
}

func (r *request) SetArguments(args []string) {
	r.arguments = args
}

func (r *request) Files() files.File {
	return r.files
}

func (r *request) SetFiles(f files.File) {
	r.files = f
}

func (r *request) Context() context.Context {
	return r.rctx
}

func (r *request) haveVarArgsFromStdin() bool {
	// we expect varargs if we have a variadic required argument and no arguments to
	// fill it
	if len(r.cmd.Arguments) == 0 {
		return false
	}

	last := r.cmd.Arguments[len(r.cmd.Arguments)-1]
	return last.SupportsStdin && last.Type == ArgString &&
		len(r.arguments) < len(r.cmd.Arguments)
}

// VarArgs can be used when you want string arguments as input
// and also want to be able to handle them in a streaming fashion
func (r *request) VarArgs(f func(string) error) error {
	var i int
	for i = 0; i < len(r.cmd.Arguments); i++ {
		if r.cmd.Arguments[i].Variadic || r.cmd.Arguments[i].SupportsStdin {
			break
		}
	}

	args := r.arguments[i:]
	if len(args) > 0 {
		for _, arg := range args {
			err := f(arg)
			if err != nil {
				return err
			}
		}

		return nil
	} else {
		if r.files != nil {
			fi, err := r.files.NextFile()
			if err != nil {
				return err
			}

			if fi.FileName() == "*stdin*" {
				fmt.Fprintln(os.Stderr, "ipfs: Reading from stdin; send Ctrl-d to stop.")
			}

			scan := bufio.NewScanner(fi)
			for scan.Scan() {
				err := f(scan.Text())
				if err != nil {
					return err
				}
			}
			return nil
		} else {
			return fmt.Errorf("expected more arguments from stdin")
		}
	}
}

func getContext(base context.Context, req Request) (context.Context, error) {
	tout, found, err := req.Option("timeout").String()
	if err != nil {
		return nil, fmt.Errorf("error parsing timeout option: %s", err)
	}

	var ctx context.Context
	if found {
		duration, err := time.ParseDuration(tout)
		if err != nil {
			return nil, fmt.Errorf("error parsing timeout option: %s", err)
		}

		tctx, _ := context.WithTimeout(base, duration)
		ctx = tctx
	} else {
		cctx, _ := context.WithCancel(base)
		ctx = cctx
	}
	return ctx, nil
}

func (r *request) InvocContext() *Context {
	return &r.ctx
}

func (r *request) SetInvocContext(ctx Context) {
	r.ctx = ctx
}

func (r *request) Command() *Command {
	return r.cmd
}

type converter func(string) (interface{}, error)

var converters = map[reflect.Kind]converter{
	Bool: func(v string) (interface{}, error) {
		if v == "" {
			return true, nil
		}
		return strconv.ParseBool(v)
	},
	Int: func(v string) (interface{}, error) {
		val, err := strconv.ParseInt(v, 0, 32)
		if err != nil {
			return nil, err
		}
		return int(val), err
	},
	Uint: func(v string) (interface{}, error) {
		val, err := strconv.ParseUint(v, 0, 32)
		if err != nil {
			return nil, err
		}
		return int(val), err
	},
	Float: func(v string) (interface{}, error) {
		return strconv.ParseFloat(v, 64)
	},
}

func (r *request) Values() map[string]interface{} {
	return r.values
}

func (r *request) Stdin() io.Reader {
	return r.stdin
}

func (r *request) ConvertOptions() error {
	for k, v := range r.options {
		opt, ok := r.optionDefs[k]
		if !ok {
			continue
		}

		kind := reflect.TypeOf(v).Kind()
		if kind != opt.Type() {
			if kind == String {
				convert := converters[opt.Type()]
				str, ok := v.(string)
				if !ok {
					return u.ErrCast()
				}
				val, err := convert(str)
				if err != nil {
					value := fmt.Sprintf("value '%v'", v)
					if len(str) == 0 {
						value = "empty value"
					}
					return fmt.Errorf("Could not convert %s to type '%s' (for option '-%s')",
						value, opt.Type().String(), k)
				}
				r.options[k] = val

			} else {
				return fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
					k, opt.Type().String(), kind.String())
			}
		} else {
			r.options[k] = v
		}

		for _, name := range opt.Names() {
			if _, ok := r.options[name]; name != k && ok {
				return fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
					k, name)
			}
		}
	}

	return nil
}

// NewEmptyRequest initializes an empty request
func NewEmptyRequest() (Request, error) {
	return NewRequest(nil, nil, nil, nil, nil, nil)
}

// NewRequest returns a request initialized with given arguments
// An non-nil error will be returned if the provided option values are invalid
func NewRequest(path []string, opts OptMap, args []string, file files.File, cmd *Command, optDefs map[string]Option) (Request, error) {
	if opts == nil {
		opts = make(OptMap)
	}
	if optDefs == nil {
		optDefs = make(map[string]Option)
	}

	ctx := Context{}
	values := make(map[string]interface{})
	req := &request{
		path:       path,
		options:    opts,
		arguments:  args,
		files:      file,
		cmd:        cmd,
		ctx:        ctx,
		optionDefs: optDefs,
		values:     values,
		stdin:      os.Stdin,
	}
	err := req.ConvertOptions()
	if err != nil {
		return nil, err
	}

	return req, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/helper/config"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type Config struct {
	Region       string `mapstructure:"region"`
	Bucket       string `mapstructure:"bucket"`
	ManifestPath string `mapstructure:"manifest"`
	BoxName      string `mapstructure:"box_name"`
	BoxDir       string `mapstructure:"box_dir"`
	Version      string `mapstructure:"version"`
	ACL          s3.ACL `mapstructure:"acl"`
	AccessKey    string `mapstructure:"access_key_id"`
	SecretKey    string `mapstructure:"secret_key"`
	common.PackerConfig    `mapstructure:",squash"`

	ctx          interpolate.Context
}

type PostProcessor struct {
	config Config
	s3     *s3.Bucket
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{"output"},
		},
	}, raws...)
	if err != nil {
		return err
	}

	errs := new(packer.MultiError)
	// required configuration
	templates := map[string]*string{
		"region":   &p.config.Region,
		"bucket":   &p.config.Bucket,
		"manifest": &p.config.ManifestPath,
		"box_name": &p.config.BoxName,
		"box_dir":  &p.config.BoxDir,
		"version":  &p.config.Version,
	}

	for key, ptr := range templates {
		if *ptr == "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("vagrant-s3 %s must be set", key))
		}
	}

	// Template process
	for key, ptr := range templates {
		if err = interpolate.Validate(*ptr, &p.config.ctx); err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error parsing %s template: %s", key, err))
		}
	}

	auth, err := aws.GetAuth(p.config.AccessKey, p.config.SecretKey)
	if err != nil {
		errs = packer.MultiErrorAppend(errs, fmt.Errorf("Unable to create Aws Authentication. Try providing keys 'access_key_id' and 'secret_key'"))
	}

	// determine region
	region, valid := aws.Regions[p.config.Region]
	if valid {
		p.s3 = s3.New(auth, region).Bucket(p.config.Bucket)
	} else {
		errs = packer.MultiErrorAppend(errs, fmt.Errorf("Invalid region specified: %s", p.config.Region))
	}

	if p.config.ACL == "" {
		p.config.ACL = "public-read"
	}

	if len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	// Only accept input from the vagrant post-processor
	if artifact.BuilderId() != "mitchellh.post-processor.vagrant" {
		return nil, false, fmt.Errorf("Unknown artifact type, requires box from vagrant post-processor: %s", artifact.BuilderId())
	}

	// Assume there is only one .box file to upload
	box := artifact.Files()[0]
	if !strings.HasSuffix(box, ".box") {
		return nil, false, fmt.Errorf("Unknown files in artifact from vagrant post-processor: %s", artifact.Files())
	}

	provider := providerFromBuilderName(artifact.Id())
	ui.Say(fmt.Sprintf("Preparing to upload box for '%s' provider to S3 bucket '%s'", provider, p.config.Bucket))

	// open the box so we can upload to S3 and calculate checksum for the manifest
	file, err := os.Open(box)
	if err != nil {
		return nil, false, err
	}
	defer file.Close()

	// get the file's size
	info, err := file.Stat()
	if err != nil {
		return nil, false, err
	}
	size := info.Size()
	ui.Message(fmt.Sprintf("Box to upload: %s (%d bytes)", box, size))

	// get the latest manifest so we can add to it
	ui.Message("Fetching latest manifest")
	manifest, err := p.getManifest()
	if err != nil {
		return nil, false, err
	}

	// generate the path to store the box in S3
	boxPath := fmt.Sprintf("%s/%s/%s", p.config.BoxDir, p.config.Version, path.Base(box))

	ui.Message("Generating checksum")
	checksum, err := sum256(file)
	if err != nil {
		return nil, false, err
	}
	ui.Message(fmt.Sprintf("Checksum is %s", checksum))

	ui.Message(fmt.Sprintf("Adding %s %s box to manifest", provider, p.config.Version))
	if err := manifest.add(p.config.Version, &Provider{
		Name:         provider,
		Url:          p.s3.URL(boxPath),
		ChecksumType: "sha256",
		Checksum:     checksum,
	}); err != nil {
		return nil, false, err
	}

	// upload the box to S3 (rewinding as we already read the file to generate the checksum)
	ui.Message(fmt.Sprintf("Uploading box to S3: %s", boxPath))
	if _, err := file.Seek(0, 0); err != nil {
		return nil, false, err
	}
	if size > 100*1024*1024 {
		ui.Message("File size > 100MB. Initiating multipart upload")
		multi, err := p.s3.Multi(boxPath, "application/octet-stream", p.config.ACL)
		if err != nil {
			return nil, false, err
		}
		parts, err := multi.PutAll(file, 5*1024*1024)
		if err != nil {
			return nil, false, err
		}
		if err := multi.Complete(parts); err != nil {
			return nil, false, err
		}
	} else {
		if err := p.s3.PutReader(boxPath, file, size, "application/octet-stream", p.config.ACL); err != nil {
			return nil, false, err
		}
	}

	ui.Message(fmt.Sprintf("Uploading the manifest: %s", p.config.ManifestPath))
	if err := p.putManifest(manifest); err != nil {
		return nil, false, err
	}

	return &Artifact{p.s3.URL(p.config.ManifestPath)}, true, nil
}

func (p *PostProcessor) getManifest() (*Manifest, error) {
	objExists, err := p.s3.Exists(p.config.ManifestPath)
	if !objExists {
		return &Manifest{Name: p.config.BoxName}, nil
	}

	body, err := p.s3.GetReader(p.config.ManifestPath)
	if err != nil {
		return nil, err
	}
	defer body.Close()

	manifest := &Manifest{}
	if err := json.NewDecoder(body).Decode(manifest); err != nil {
		return nil, err
	}
	return manifest, nil
}

func (p *PostProcessor) putManifest(manifest *Manifest) error {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(manifest); err != nil {
		return err
	}
	if err := p.s3.Put(p.config.ManifestPath, buf.Bytes(), "application/json", p.config.ACL); err != nil {
		return err
	}
	return nil
}

// calculates a sha256 checksum of the file
func sum256(file *os.File) (string, error) {
	h := sha256.New()
	if _, err := io.Copy(h, file); err != nil {
		return "", err
	}
	return hex.EncodeToString(h.Sum(nil)), nil
}

// converts a packer builder name to the corresponding vagrant provider
func providerFromBuilderName(name string) string {
	switch name {
	case "aws":
		return "aws"
	case "digitalocean":
		return "digitalocean"
	case "virtualbox":
		return "virtualbox"
	case "vmware":
		return "vmware_desktop"
	case "parallels":
		return "parallels"
	default:
		return name
	}
}
<commit_msg>Fixed the error with non-existent manifest.<commit_after>package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/helper/config"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type Config struct {
	Region       string `mapstructure:"region"`
	Bucket       string `mapstructure:"bucket"`
	ManifestPath string `mapstructure:"manifest"`
	BoxName      string `mapstructure:"box_name"`
	BoxDir       string `mapstructure:"box_dir"`
	Version      string `mapstructure:"version"`
	ACL          s3.ACL `mapstructure:"acl"`
	AccessKey    string `mapstructure:"access_key_id"`
	SecretKey    string `mapstructure:"secret_key"`
	common.PackerConfig    `mapstructure:",squash"`

	ctx          interpolate.Context
}

type PostProcessor struct {
	config Config
	s3     *s3.Bucket
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{"output"},
		},
	}, raws...)
	if err != nil {
		return err
	}

	errs := new(packer.MultiError)
	// required configuration
	templates := map[string]*string{
		"region":   &p.config.Region,
		"bucket":   &p.config.Bucket,
		"manifest": &p.config.ManifestPath,
		"box_name": &p.config.BoxName,
		"box_dir":  &p.config.BoxDir,
		"version":  &p.config.Version,
	}

	for key, ptr := range templates {
		if *ptr == "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("vagrant-s3 %s must be set", key))
		}
	}

	// Template process
	for key, ptr := range templates {
		if err = interpolate.Validate(*ptr, &p.config.ctx); err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error parsing %s template: %s", key, err))
		}
	}

	auth, err := aws.GetAuth(p.config.AccessKey, p.config.SecretKey)
	if err != nil {
		errs = packer.MultiErrorAppend(errs, fmt.Errorf("Unable to create Aws Authentication. Try providing keys 'access_key_id' and 'secret_key'"))
	}

	// determine region
	region, valid := aws.Regions[p.config.Region]
	if valid {
		p.s3 = s3.New(auth, region).Bucket(p.config.Bucket)
	} else {
		errs = packer.MultiErrorAppend(errs, fmt.Errorf("Invalid region specified: %s", p.config.Region))
	}

	if p.config.ACL == "" {
		p.config.ACL = "public-read"
	}

	if len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	// Only accept input from the vagrant post-processor
	if artifact.BuilderId() != "mitchellh.post-processor.vagrant" {
		return nil, false, fmt.Errorf("Unknown artifact type, requires box from vagrant post-processor: %s", artifact.BuilderId())
	}

	// Assume there is only one .box file to upload
	box := artifact.Files()[0]
	if !strings.HasSuffix(box, ".box") {
		return nil, false, fmt.Errorf("Unknown files in artifact from vagrant post-processor: %s", artifact.Files())
	}

	provider := providerFromBuilderName(artifact.Id())
	ui.Say(fmt.Sprintf("Preparing to upload box for '%s' provider to S3 bucket '%s'", provider, p.config.Bucket))

	// open the box so we can upload to S3 and calculate checksum for the manifest
	file, err := os.Open(box)
	if err != nil {
		return nil, false, err
	}
	defer file.Close()

	// get the file's size
	info, err := file.Stat()
	if err != nil {
		return nil, false, err
	}
	size := info.Size()
	ui.Message(fmt.Sprintf("Box to upload: %s (%d bytes)", box, size))

	// get the latest manifest so we can add to it
	ui.Message("Fetching latest manifest")
	manifest, err := p.getManifest()
	if err != nil {
		return nil, false, err
	}

	// generate the path to store the box in S3
	boxPath := fmt.Sprintf("%s/%s/%s", p.config.BoxDir, p.config.Version, path.Base(box))

	ui.Message("Generating checksum")
	checksum, err := sum256(file)
	if err != nil {
		return nil, false, err
	}
	ui.Message(fmt.Sprintf("Checksum is %s", checksum))

	ui.Message(fmt.Sprintf("Adding %s %s box to manifest", provider, p.config.Version))
	if err := manifest.add(p.config.Version, &Provider{
		Name:         provider,
		Url:          p.s3.URL(boxPath),
		ChecksumType: "sha256",
		Checksum:     checksum,
	}); err != nil {
		return nil, false, err
	}

	// upload the box to S3 (rewinding as we already read the file to generate the checksum)
	ui.Message(fmt.Sprintf("Uploading box to S3: %s", boxPath))
	if _, err := file.Seek(0, 0); err != nil {
		return nil, false, err
	}
	if size > 100*1024*1024 {
		ui.Message("File size > 100MB. Initiating multipart upload")
		multi, err := p.s3.Multi(boxPath, "application/octet-stream", p.config.ACL)
		if err != nil {
			return nil, false, err
		}
		parts, err := multi.PutAll(file, 5*1024*1024)
		if err != nil {
			return nil, false, err
		}
		if err := multi.Complete(parts); err != nil {
			return nil, false, err
		}
	} else {
		if err := p.s3.PutReader(boxPath, file, size, "application/octet-stream", p.config.ACL); err != nil {
			return nil, false, err
		}
	}

	ui.Message(fmt.Sprintf("Uploading the manifest: %s", p.config.ManifestPath))
	if err := p.putManifest(manifest); err != nil {
		return nil, false, err
	}

	return &Artifact{p.s3.URL(p.config.ManifestPath)}, true, nil
}

func (p *PostProcessor) getManifest() (*Manifest, error) {
	body, err := p.s3.GetReader(p.config.ManifestPath)
	if err != nil {
		s3Err, ok := err.(*s3.Error);
		if  ok && s3Err.Code == "NoSuchKey" {
			return &Manifest{Name: p.config.BoxName}, nil
		}
		return nil, err
	}
	defer body.Close()

	manifest := &Manifest{}
	if err := json.NewDecoder(body).Decode(manifest); err != nil {
		return nil, err
	}
	return manifest, nil
}

func (p *PostProcessor) putManifest(manifest *Manifest) error {
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(manifest); err != nil {
		return err
	}
	if err := p.s3.Put(p.config.ManifestPath, buf.Bytes(), "application/json", p.config.ACL); err != nil {
		return err
	}
	return nil
}

// calculates a sha256 checksum of the file
func sum256(file *os.File) (string, error) {
	h := sha256.New()
	if _, err := io.Copy(h, file); err != nil {
		return "", err
	}
	return hex.EncodeToString(h.Sum(nil)), nil
}

// converts a packer builder name to the corresponding vagrant provider
func providerFromBuilderName(name string) string {
	switch name {
	case "aws":
		return "aws"
	case "digitalocean":
		return "digitalocean"
	case "virtualbox":
		return "virtualbox"
	case "vmware":
		return "vmware_desktop"
	case "parallels":
		return "parallels"
	default:
		return name
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"

	"github.com/keybase/kbfs/dokan/winacl"
	"golang.org/x/net/context"
)

// Config is the configuration used for a mount.
type Config struct {
	// Path is the path to mount, e.g. `L:`. Must be set.
	Path string
	// FileSystem is the filesystem implementation. Must be set.
	FileSystem FileSystem
	// MountFlags for this filesystem instance. Is optional.
	MountFlags MountFlag
	// DllPath is the optional full path to dokan1.dll.
	// Empty causes dokan1.dll to be loaded from the system directory.
	// Only the first load of a dll determines the path -
	// further instances in the same process will use
	// the same instance regardless of path.
	DllPath string
}

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	// WithContext returns a context for a new request. If the CancelFunc
	// is not null, it is called after the request is done. The most minimal
	// implementation is
	// `func (*T)WithContext(c context.Context) { return c, nil }`.
	WithContext(context.Context) (context.Context, context.CancelFunc)

	// CreateFile is called to open and create files.
	CreateFile(ctx context.Context, fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)

	// GetDiskFreeSpace returns information about disk free space.
	// Called quite often by Explorer.
	GetDiskFreeSpace(ctx context.Context) (FreeSpace, error)

	// GetVolumeInformation returns information about the volume.
	GetVolumeInformation(ctx context.Context) (VolumeInformation, error)

	// MoveFile corresponds to rename.
	MoveFile(ctx context.Context, source *FileInfo, targetPath string, replaceExisting bool) error

	// ErrorPrint is called when dokan needs notify the program of an error message.
	// A sensible approach is to print the error.
	ErrorPrint(error)
}

// MountFlag is the type for Dokan mount flags.
type MountFlag uint32

// Flags for mounting the filesystem. See Dokan documentation for these.
const (
	CDebug         = MountFlag(kbfsLibdokanDebug)
	CStderr        = MountFlag(kbfsLibdokanStderr)
	Removable      = MountFlag(kbfsLibdokanRemovable)
	MountManager   = MountFlag(kbfsLibdokanMountManager)
	CurrentSession = MountFlag(kbfsLibdokanCurrentSession)
	// UseFindFilesWithPattern enables FindFiles calls to be with a search
	// pattern string. Otherwise the string will be empty in all calls.
	UseFindFilesWithPattern = MountFlag(kbfsLibdokanUseFindFilesWithPattern)
)

// CreateData contains all the info needed to create a file.
type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    FileAttribute
	ShareAccess       uint32
	CreateDisposition CreateDisposition
	CreateOptions     uint32
}

// ReturningFileAllowed answers whether returning a file is allowed by
// CreateOptions.
func (cd *CreateData) ReturningFileAllowed() error {
	if cd.CreateOptions&FileDirectoryFile != 0 {
		return ErrNotADirectory
	}
	return nil
}

// ReturningFileAllowed answers whether returning a directory is allowed by
// CreateOptions.
func (cd *CreateData) ReturningDirAllowed() error {
	if cd.CreateOptions&FileNonDirectoryFile != 0 {
		return ErrFileIsADirectory
	}
	return nil
}

// CreateDisposition marks whether to create or open a file. Not a bitmask.
type CreateDisposition uint32

// File creation flags for CreateFile. This is not a bitmask.
const (
	FileSupersede   = CreateDisposition(0)
	FileOpen        = CreateDisposition(1)
	FileCreate      = CreateDisposition(2)
	FileOpenIf      = CreateDisposition(3)
	FileOverwrite   = CreateDisposition(4)
	FileOverwriteIf = CreateDisposition(5)
)

// CreateOptions flags. These are bitmask flags.
const (
	FileDirectoryFile    = 0x1
	FileNonDirectoryFile = 0x40
	FileOpenReparsePoint = 0x00200000
)

// FileAttribute is the type of a directory entry in Stat.
type FileAttribute uint32

// File attribute bit masks - same as syscall but provided for all platforms.
const (
	FileAttributeReadonly     = FileAttribute(0x00000001)
	FileAttributeHidden       = FileAttribute(0x00000002)
	FileAttributeSystem       = FileAttribute(0x00000004)
	FileAttributeDirectory    = FileAttribute(0x00000010)
	FileAttributeArchive      = FileAttribute(0x00000020)
	FileAttributeNormal       = FileAttribute(0x00000080)
	FileAttributeReparsePoint = FileAttribute(0x00000400)
	IOReparseTagSymlink       = 0xA000000C
)

// File is the interface for files and directories.
type File interface {
	// ReadFile implements read for dokan.
	ReadFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// WriteFile implements write for dokan.
	WriteFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// FlushFileBuffers corresponds to fsync.
	FlushFileBuffers(ctx context.Context, fi *FileInfo) error

	// GetFileInformation - corresponds to stat.
	GetFileInformation(ctx context.Context, fi *FileInfo) (*Stat, error)

	// FindFiles is the readdir. The function is a callback that should be called
	// with each file. The same NamedStat may be reused for subsequent calls.
	//
	// Pattern will be an empty string unless UseFindFilesWithPattern is enabled - then
	// it may be a pattern like `*.png` to match. All implementations must be prepared
	// to handle empty strings as patterns.
	FindFiles(ctx context.Context, fi *FileInfo, pattern string, fillStatCallback func(*NamedStat) error) error

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(ctx context.Context, fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	// SetFileAttributes is for setting file attributes.
	SetFileAttributes(ctx context.Context, fi *FileInfo, fileAttributes FileAttribute) error

	// SetEndOfFile truncates the file. May be used to extend a file with zeros.
	SetEndOfFile(ctx context.Context, fi *FileInfo, length int64) error
	// SetAllocationSize see FILE_ALLOCATION_INFORMATION on MSDN.
	// For simple semantics if length > filesize then ignore else truncate(length).
	SetAllocationSize(ctx context.Context, fi *FileInfo, length int64) error

	LockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
	UnlockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error

	GetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
	SetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.IsDeleteOnClose in Cleanup.
	CanDeleteFile(ctx context.Context, fi *FileInfo) error
	CanDeleteDirectory(ctx context.Context, fi *FileInfo) error
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.IsDeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(ctx context.Context, fi *FileInfo)
	// CloseFile is called when closing a handle to the file.
	CloseFile(ctx context.Context, fi *FileInfo)
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsRemoteStorage      = FileSystemFlags(0x100)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// FileSize is the size of the file in bytes
	FileSize int64
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// FileAttributes bitmask holds the file attributes.
	FileAttributes FileAttribute
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// NumberOfLinks can be omitted, if zero set to 1.
	NumberOfLinks uint32
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a DOS-name, insert the corresponding
// DOS-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtStatus is a type implementing error interface that corresponds
// to NTSTATUS. It can be used to set the exact error/status code
// from the filesystem.
type NtStatus uint32

func (n NtStatus) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtStatus(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtStatus(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtStatus(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtStatus(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtStatus(0xC00000BB)
	// ErrFileIsADirectory - file is a directory.
	ErrFileIsADirectory = NtStatus(0xC00000BA)
	// ErrDirectoryNotEmpty - wanted an empty dir - it is not empty.
	ErrDirectoryNotEmpty = NtStatus(0xC0000101)
	// ErrNotADirectory - wanted a directory - it is not a directory.
	ErrNotADirectory = NtStatus(0xC0000103)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtStatus(0xC0000035)
	// ErrNotSameDevice - MoveFile is denied, please use copy+delete.
	ErrNotSameDevice = NtStatus(0xC00000D4)
	// StatusBufferOverflow - buffer space too short for return value.
	StatusBufferOverflow = NtStatus(0x80000005)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtStatus(0x40000000)
)
<commit_msg>dokan: Fix doc comment<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"

	"github.com/keybase/kbfs/dokan/winacl"
	"golang.org/x/net/context"
)

// Config is the configuration used for a mount.
type Config struct {
	// Path is the path to mount, e.g. `L:`. Must be set.
	Path string
	// FileSystem is the filesystem implementation. Must be set.
	FileSystem FileSystem
	// MountFlags for this filesystem instance. Is optional.
	MountFlags MountFlag
	// DllPath is the optional full path to dokan1.dll.
	// Empty causes dokan1.dll to be loaded from the system directory.
	// Only the first load of a dll determines the path -
	// further instances in the same process will use
	// the same instance regardless of path.
	DllPath string
}

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	// WithContext returns a context for a new request. If the CancelFunc
	// is not null, it is called after the request is done. The most minimal
	// implementation is
	// `func (*T)WithContext(c context.Context) { return c, nil }`.
	WithContext(context.Context) (context.Context, context.CancelFunc)

	// CreateFile is called to open and create files.
	CreateFile(ctx context.Context, fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)

	// GetDiskFreeSpace returns information about disk free space.
	// Called quite often by Explorer.
	GetDiskFreeSpace(ctx context.Context) (FreeSpace, error)

	// GetVolumeInformation returns information about the volume.
	GetVolumeInformation(ctx context.Context) (VolumeInformation, error)

	// MoveFile corresponds to rename.
	MoveFile(ctx context.Context, source *FileInfo, targetPath string, replaceExisting bool) error

	// ErrorPrint is called when dokan needs notify the program of an error message.
	// A sensible approach is to print the error.
	ErrorPrint(error)
}

// MountFlag is the type for Dokan mount flags.
type MountFlag uint32

// Flags for mounting the filesystem. See Dokan documentation for these.
const (
	CDebug         = MountFlag(kbfsLibdokanDebug)
	CStderr        = MountFlag(kbfsLibdokanStderr)
	Removable      = MountFlag(kbfsLibdokanRemovable)
	MountManager   = MountFlag(kbfsLibdokanMountManager)
	CurrentSession = MountFlag(kbfsLibdokanCurrentSession)
	// UseFindFilesWithPattern enables FindFiles calls to be with a search
	// pattern string. Otherwise the string will be empty in all calls.
	UseFindFilesWithPattern = MountFlag(kbfsLibdokanUseFindFilesWithPattern)
)

// CreateData contains all the info needed to create a file.
type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    FileAttribute
	ShareAccess       uint32
	CreateDisposition CreateDisposition
	CreateOptions     uint32
}

// ReturningFileAllowed answers whether returning a file is allowed by
// CreateOptions.
func (cd *CreateData) ReturningFileAllowed() error {
	if cd.CreateOptions&FileDirectoryFile != 0 {
		return ErrNotADirectory
	}
	return nil
}

// ReturningDirAllowed answers whether returning a directory is allowed by
// CreateOptions.
func (cd *CreateData) ReturningDirAllowed() error {
	if cd.CreateOptions&FileNonDirectoryFile != 0 {
		return ErrFileIsADirectory
	}
	return nil
}

// CreateDisposition marks whether to create or open a file. Not a bitmask.
type CreateDisposition uint32

// File creation flags for CreateFile. This is not a bitmask.
const (
	FileSupersede   = CreateDisposition(0)
	FileOpen        = CreateDisposition(1)
	FileCreate      = CreateDisposition(2)
	FileOpenIf      = CreateDisposition(3)
	FileOverwrite   = CreateDisposition(4)
	FileOverwriteIf = CreateDisposition(5)
)

// CreateOptions flags. These are bitmask flags.
const (
	FileDirectoryFile    = 0x1
	FileNonDirectoryFile = 0x40
	FileOpenReparsePoint = 0x00200000
)

// FileAttribute is the type of a directory entry in Stat.
type FileAttribute uint32

// File attribute bit masks - same as syscall but provided for all platforms.
const (
	FileAttributeReadonly     = FileAttribute(0x00000001)
	FileAttributeHidden       = FileAttribute(0x00000002)
	FileAttributeSystem       = FileAttribute(0x00000004)
	FileAttributeDirectory    = FileAttribute(0x00000010)
	FileAttributeArchive      = FileAttribute(0x00000020)
	FileAttributeNormal       = FileAttribute(0x00000080)
	FileAttributeReparsePoint = FileAttribute(0x00000400)
	IOReparseTagSymlink       = 0xA000000C
)

// File is the interface for files and directories.
type File interface {
	// ReadFile implements read for dokan.
	ReadFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// WriteFile implements write for dokan.
	WriteFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
	// FlushFileBuffers corresponds to fsync.
	FlushFileBuffers(ctx context.Context, fi *FileInfo) error

	// GetFileInformation - corresponds to stat.
	GetFileInformation(ctx context.Context, fi *FileInfo) (*Stat, error)

	// FindFiles is the readdir. The function is a callback that should be called
	// with each file. The same NamedStat may be reused for subsequent calls.
	//
	// Pattern will be an empty string unless UseFindFilesWithPattern is enabled - then
	// it may be a pattern like `*.png` to match. All implementations must be prepared
	// to handle empty strings as patterns.
	FindFiles(ctx context.Context, fi *FileInfo, pattern string, fillStatCallback func(*NamedStat) error) error

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(ctx context.Context, fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	// SetFileAttributes is for setting file attributes.
	SetFileAttributes(ctx context.Context, fi *FileInfo, fileAttributes FileAttribute) error

	// SetEndOfFile truncates the file. May be used to extend a file with zeros.
	SetEndOfFile(ctx context.Context, fi *FileInfo, length int64) error
	// SetAllocationSize see FILE_ALLOCATION_INFORMATION on MSDN.
	// For simple semantics if length > filesize then ignore else truncate(length).
	SetAllocationSize(ctx context.Context, fi *FileInfo, length int64) error

	LockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
	UnlockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error

	GetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
	SetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.IsDeleteOnClose in Cleanup.
	CanDeleteFile(ctx context.Context, fi *FileInfo) error
	CanDeleteDirectory(ctx context.Context, fi *FileInfo) error
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.IsDeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(ctx context.Context, fi *FileInfo)
	// CloseFile is called when closing a handle to the file.
	CloseFile(ctx context.Context, fi *FileInfo)
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsRemoteStorage      = FileSystemFlags(0x100)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// FileSize is the size of the file in bytes
	FileSize int64
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// FileAttributes bitmask holds the file attributes.
	FileAttributes FileAttribute
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// NumberOfLinks can be omitted, if zero set to 1.
	NumberOfLinks uint32
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a DOS-name, insert the corresponding
// DOS-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtStatus is a type implementing error interface that corresponds
// to NTSTATUS. It can be used to set the exact error/status code
// from the filesystem.
type NtStatus uint32

func (n NtStatus) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtStatus(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtStatus(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtStatus(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtStatus(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtStatus(0xC00000BB)
	// ErrFileIsADirectory - file is a directory.
	ErrFileIsADirectory = NtStatus(0xC00000BA)
	// ErrDirectoryNotEmpty - wanted an empty dir - it is not empty.
	ErrDirectoryNotEmpty = NtStatus(0xC0000101)
	// ErrNotADirectory - wanted a directory - it is not a directory.
	ErrNotADirectory = NtStatus(0xC0000103)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtStatus(0xC0000035)
	// ErrNotSameDevice - MoveFile is denied, please use copy+delete.
	ErrNotSameDevice = NtStatus(0xC00000D4)
	// StatusBufferOverflow - buffer space too short for return value.
	StatusBufferOverflow = NtStatus(0x80000005)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtStatus(0x40000000)
)
<|endoftext|>