<commit_before>package consul

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/vault/logical"
	logicaltest "github.com/hashicorp/vault/logical/testing"
	"github.com/mitchellh/mapstructure"
)

func TestBackend_basic(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	config, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepConfig(t, config),
			testAccStepWritePolicy(t, "test", testPolicy, ""),
			testAccStepReadToken(t, "test", config),
		},
	})
}

func TestBackend_management(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	config, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepConfig(t, config),
			testAccStepWriteManagementPolicy(t, "test", ""),
			testAccStepReadManagementToken(t, "test", config),
		},
	})
}

func TestBackend_crud(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	_, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepWritePolicy(t, "test", testPolicy, ""),
			testAccStepReadPolicy(t, "test", testPolicy, 0),
			testAccStepDeletePolicy(t, "test"),
		},
	})
}

func TestBackend_role_lease(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	_, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepWritePolicy(t, "test", testPolicy, "6h"),
			testAccStepReadPolicy(t, "test", testPolicy, 6*time.Hour),
			testAccStepDeletePolicy(t, "test"),
		},
	})
}

func testStartConsulServer(t *testing.T) (map[string]interface{}, *os.Process) {
	if _, err := exec.LookPath("consul"); err != nil {
		t.Errorf("consul not found: %s", err)
	}

	td, err := ioutil.TempDir("", "vault")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	tf, err := ioutil.TempFile("", "vault")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if _, err := tf.Write([]byte(strings.TrimSpace(testConsulConfig))); err != nil {
		t.Fatalf("err: %s", err)
	}
	tf.Close()

	cmd := exec.Command(
		"consul", "agent",
		"-server",
		"-bootstrap",
		"-advertise", "127.0.0.1",
		"-config-file", tf.Name(),
		"-data-dir", td)
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	stdoutScanner := bufio.NewScanner(stdout)
	stderrScanner := bufio.NewScanner(stderr)
	stdoutScanFunc := func() {
		for stdoutScanner.Scan() {
			t.Logf("Consul stdout: %s\n", stdoutScanner.Text())
		}
	}
	stderrScanFunc := func() {
		for stderrScanner.Scan() {
			t.Logf("Consul stderr: %s\n", stderrScanner.Text())
		}
	}
	if os.Getenv("VAULT_VERBOSE_ACC_TESTS") != "" {
		go stdoutScanFunc()
		go stderrScanFunc()
	}

	if err := cmd.Start(); err != nil {
		t.Fatalf("error starting Consul: %s", err)
	}
	// Give Consul time to startup
	time.Sleep(2 * time.Second)

	config := map[string]interface{}{
		"address": "127.0.0.1:8500",
		"token":   "test",
	}
	return config, cmd.Process
}

func testStopConsulServer(t *testing.T, p *os.Process) {
	p.Kill()
}

func testAccPreCheck(t *testing.T) {
	if _, err := exec.LookPath("consul"); err != nil {
		t.Fatal("consul must be on PATH")
	}
}

func testAccStepConfig(
	t *testing.T, config map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "config/access",
		Data:      config,
	}
}

func testAccStepReadToken(
	t *testing.T, name string, conf map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "creds/" + name,
		Check: func(resp *logical.Response) error {
			var d struct {
				Token string `mapstructure:"token"`
			}
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
				return err
			}
			log.Printf("[WARN] Generated token: %s", d.Token)

			// Build a client and verify that the credentials work
			config := api.DefaultConfig()
			config.Address = conf["address"].(string)
			config.Token = d.Token
			client, err := api.NewClient(config)
			if err != nil {
				return err
			}

			log.Printf("[WARN] Verifying that the generated token works...")
			_, err = client.KV().Put(&api.KVPair{
				Key:   "foo",
				Value: []byte("bar"),
			}, nil)
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func testAccStepReadManagementToken(
	t *testing.T, name string, conf map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "creds/" + name,
		Check: func(resp *logical.Response) error {
			var d struct {
				Token string `mapstructure:"token"`
			}
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
				return err
			}
			log.Printf("[WARN] Generated token: %s", d.Token)

			// Build a client and verify that the credentials work
			config := api.DefaultConfig()
			config.Address = conf["address"].(string)
			config.Token = d.Token
			client, err := api.NewClient(config)
			if err != nil {
				return err
			}

			log.Printf("[WARN] Verifying that the generated token works...")
			_, _, err = client.ACL().Create(&api.ACLEntry{
				Type: "management",
				Name: "test2",
			}, nil)
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func testAccStepWritePolicy(t *testing.T, name string, policy string, lease string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "roles/" + name,
		Data: map[string]interface{}{
			"policy": base64.StdEncoding.EncodeToString([]byte(policy)),
			"lease":  lease,
		},
	}
}

func testAccStepWriteManagementPolicy(t *testing.T, name string, lease string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "roles/" + name,
		Data: map[string]interface{}{
			"token_type": "management",
			"lease":      lease,
		},
	}
}

func testAccStepReadPolicy(t *testing.T, name string, policy string, lease time.Duration) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "roles/" + name,
		Check: func(resp *logical.Response) error {
			policyRaw := resp.Data["policy"].(string)
			out, err := base64.StdEncoding.DecodeString(policyRaw)
			if err != nil {
				return err
			}
			if string(out) != policy {
				return fmt.Errorf("mismatch: %s %s", out, policy)
			}

			leaseRaw := resp.Data["lease"].(string)
			l, err := time.ParseDuration(leaseRaw)
			if err != nil {
				return err
			}
			if l != lease {
				return fmt.Errorf("mismatch: %v %v", l, lease)
			}
			return nil
		},
	}
}

func testAccStepDeletePolicy(t *testing.T, name string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.DeleteOperation,
		Path:      "roles/" + name,
	}
}

const testPolicy = `
key "" {
	policy = "write"
}
`

const testConsulConfig = `
{
	"datacenter": "test",
	"acl_datacenter": "test",
	"acl_master_token": "test"
}
`
<commit_msg>Add test for renew/revoke to Consul secret backend<commit_after>package consul

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/vault/logical"
	logicaltest "github.com/hashicorp/vault/logical/testing"
	"github.com/mitchellh/mapstructure"
)

func TestBackend_basic(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	config, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepConfig(t, config),
			testAccStepWritePolicy(t, "test", testPolicy, ""),
			testAccStepReadToken(t, "test", config),
		},
	})
}

func TestBackend_renew_revoke(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	config, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	beConfig := logical.TestBackendConfig()
	beConfig.StorageView = &logical.InmemStorage{}
	b, _ := Factory(beConfig)

	req := &logical.Request{
		Storage:   beConfig.StorageView,
		Operation: logical.UpdateOperation,
		Path:      "config/access",
		Data:      config,
	}
	resp, err := b.HandleRequest(req)
	if err != nil {
		t.Fatal(err)
	}

	req.Path = "roles/test"
	req.Data = map[string]interface{}{
		"policy": base64.StdEncoding.EncodeToString([]byte(testPolicy)),
		"lease":  "6h",
	}
	resp, err = b.HandleRequest(req)
	if err != nil {
		t.Fatal(err)
	}

	req.Operation = logical.ReadOperation
	req.Path = "creds/test"
	resp, err = b.HandleRequest(req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil || resp.IsError() {
		t.Fatal("resp nil or error")
	}

	generatedSecret := resp.Secret
	generatedSecret.IssueTime = time.Now()
	generatedSecret.TTL = 6 * time.Hour

	var d struct {
		Token string `mapstructure:"token"`
	}
	if err := mapstructure.Decode(resp.Data, &d); err != nil {
		t.Fatal(err)
	}
	log.Printf("[WARN] Generated token: %s", d.Token)

	// Build a client and verify that the credentials work
	apiConfig := api.DefaultConfig()
	apiConfig.Address = config["address"].(string)
	apiConfig.Token = d.Token
	client, err := api.NewClient(apiConfig)
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("[WARN] Verifying that the generated token works...")
	_, err = client.KV().Put(&api.KVPair{
		Key:   "foo",
		Value: []byte("bar"),
	}, nil)
	if err != nil {
		t.Fatal(err)
	}

	req.Operation = logical.RenewOperation
	req.Secret = generatedSecret
	resp, err = b.HandleRequest(req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("got nil response from renew")
	}

	req.Operation = logical.RevokeOperation
	resp, err = b.HandleRequest(req)
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("[WARN] Verifying that the generated token does not work...")
	_, err = client.KV().Put(&api.KVPair{
		Key:   "foo",
		Value: []byte("bar"),
	}, nil)
	if err == nil {
		t.Fatal("expected error")
	}
}

func TestBackend_management(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	config, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepConfig(t, config),
			testAccStepWriteManagementPolicy(t, "test", ""),
			testAccStepReadManagementToken(t, "test", config),
		},
	})
}

func TestBackend_crud(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	_, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepWritePolicy(t, "test", testPolicy, ""),
			testAccStepReadPolicy(t, "test", testPolicy, 0),
			testAccStepDeletePolicy(t, "test"),
		},
	})
}

func TestBackend_role_lease(t *testing.T) {
	if os.Getenv(logicaltest.TestEnvVar) == "" {
		t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", logicaltest.TestEnvVar))
		return
	}

	_, process := testStartConsulServer(t)
	defer testStopConsulServer(t, process)

	b, _ := Factory(logical.TestBackendConfig())
	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		PreCheck:       func() { testAccPreCheck(t) },
		Backend:        b,
		Steps: []logicaltest.TestStep{
			testAccStepWritePolicy(t, "test", testPolicy, "6h"),
			testAccStepReadPolicy(t, "test", testPolicy, 6*time.Hour),
			testAccStepDeletePolicy(t, "test"),
		},
	})
}

func testStartConsulServer(t *testing.T) (map[string]interface{}, *os.Process) {
	if _, err := exec.LookPath("consul"); err != nil {
		t.Errorf("consul not found: %s", err)
	}

	td, err := ioutil.TempDir("", "vault")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	tf, err := ioutil.TempFile("", "vault")
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if _, err := tf.Write([]byte(strings.TrimSpace(testConsulConfig))); err != nil {
		t.Fatalf("err: %s", err)
	}
	tf.Close()

	cmd := exec.Command(
		"consul", "agent",
		"-server",
		"-bootstrap",
		"-advertise", "127.0.0.1",
		"-config-file", tf.Name(),
		"-data-dir", td)
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	stdoutScanner := bufio.NewScanner(stdout)
	stderrScanner := bufio.NewScanner(stderr)
	stdoutScanFunc := func() {
		for stdoutScanner.Scan() {
			t.Logf("Consul stdout: %s\n", stdoutScanner.Text())
		}
	}
	stderrScanFunc := func() {
		for stderrScanner.Scan() {
			t.Logf("Consul stderr: %s\n", stderrScanner.Text())
		}
	}
	if os.Getenv("VAULT_VERBOSE_ACC_TESTS") != "" {
		go stdoutScanFunc()
		go stderrScanFunc()
	}

	if err := cmd.Start(); err != nil {
		t.Fatalf("error starting Consul: %s", err)
	}
	// Give Consul time to startup
	time.Sleep(2 * time.Second)

	config := map[string]interface{}{
		"address": "127.0.0.1:8500",
		"token":   "test",
	}
	return config, cmd.Process
}

func testStopConsulServer(t *testing.T, p *os.Process) {
	p.Kill()
}

func testAccPreCheck(t *testing.T) {
	if _, err := exec.LookPath("consul"); err != nil {
		t.Fatal("consul must be on PATH")
	}
}

func testAccStepConfig(
	t *testing.T, config map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "config/access",
		Data:      config,
	}
}

func testAccStepReadToken(
	t *testing.T, name string, conf map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "creds/" + name,
		Check: func(resp *logical.Response) error {
			var d struct {
				Token string `mapstructure:"token"`
			}
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
				return err
			}
			log.Printf("[WARN] Generated token: %s", d.Token)

			// Build a client and verify that the credentials work
			config := api.DefaultConfig()
			config.Address = conf["address"].(string)
			config.Token = d.Token
			client, err := api.NewClient(config)
			if err != nil {
				return err
			}

			log.Printf("[WARN] Verifying that the generated token works...")
			_, err = client.KV().Put(&api.KVPair{
				Key:   "foo",
				Value: []byte("bar"),
			}, nil)
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func testAccStepReadManagementToken(
	t *testing.T, name string, conf map[string]interface{}) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "creds/" + name,
		Check: func(resp *logical.Response) error {
			var d struct {
				Token string `mapstructure:"token"`
			}
			if err := mapstructure.Decode(resp.Data, &d); err != nil {
				return err
			}
			log.Printf("[WARN] Generated token: %s", d.Token)

			// Build a client and verify that the credentials work
			config := api.DefaultConfig()
			config.Address = conf["address"].(string)
			config.Token = d.Token
			client, err := api.NewClient(config)
			if err != nil {
				return err
			}

			log.Printf("[WARN] Verifying that the generated token works...")
			_, _, err = client.ACL().Create(&api.ACLEntry{
				Type: "management",
				Name: "test2",
			}, nil)
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func testAccStepWritePolicy(t *testing.T, name string, policy string, lease string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "roles/" + name,
		Data: map[string]interface{}{
			"policy": base64.StdEncoding.EncodeToString([]byte(policy)),
			"lease":  lease,
		},
	}
}

func testAccStepWriteManagementPolicy(t *testing.T, name string, lease string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "roles/" + name,
		Data: map[string]interface{}{
			"token_type": "management",
			"lease":      lease,
		},
	}
}

func testAccStepReadPolicy(t *testing.T, name string, policy string, lease time.Duration) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.ReadOperation,
		Path:      "roles/" + name,
		Check: func(resp *logical.Response) error {
			policyRaw := resp.Data["policy"].(string)
			out, err := base64.StdEncoding.DecodeString(policyRaw)
			if err != nil {
				return err
			}
			if string(out) != policy {
				return fmt.Errorf("mismatch: %s %s", out, policy)
			}

			leaseRaw := resp.Data["lease"].(string)
			l, err := time.ParseDuration(leaseRaw)
			if err != nil {
				return err
			}
			if l != lease {
				return fmt.Errorf("mismatch: %v %v", l, lease)
			}
			return nil
		},
	}
}

func testAccStepDeletePolicy(t *testing.T, name string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.DeleteOperation,
		Path:      "roles/" + name,
	}
}

const testPolicy = `
key "" {
	policy = "write"
}
`

const testConsulConfig = `
{
	"datacenter": "test",
	"acl_datacenter": "test",
	"acl_master_token": "test"
}
`
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/tsuru/gnuflag"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/safe"
)

type AppBuild struct {
	cmd.GuessingCommand
	tag string
	fs  *gnuflag.FlagSet
}

func (c *AppBuild) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		tag := "The image tag"
		c.fs.StringVar(&c.tag, "tag", "", tag)
		c.fs.StringVar(&c.tag, "t", "", tag)
	}
	return c.fs
}

func (c *AppBuild) Info() *cmd.Info {
	desc := `Builds a tsuru app image respecting .tsuruignore file. Some examples of calls are:

::

		$ tsuru app-build -a myapp -t mytag .
		$ tsuru app-build -a myapp -t latest myfile.jar Procfile
`
	return &cmd.Info{
		Name:    "app-build",
		Usage:   "app-build [-a/--app <appname>] [-t/--tag <image_tag>] <file-or-dir-1> [file-or-dir-2] ... [file-or-dir-n]",
		Desc:    desc,
		MinArgs: 0,
	}
}

func (c *AppBuild) Run(context *cmd.Context, client *cmd.Client) error {
	context.RawOutput()
	if c.tag == "" {
		return errors.New("You should provide one tag to build the image.\n")
	}
	if len(context.Args) == 0 {
		return errors.New("You should provide at least one file to build the image.\n")
	}
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	u, err := cmd.GetURL("/apps/" + appName)
	if err != nil {
		return err
	}
	request, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	values := url.Values{}
	values.Set("tag", c.tag)
	u, err = cmd.GetURLVersion("1.5", fmt.Sprintf("/apps/%s/build", appName))
	if err != nil {
		return err
	}
	body := safe.NewBuffer(nil)
	request, err = http.NewRequest("POST", u, body)
	if err != nil {
		return err
	}
	buf := safe.NewBuffer(nil)
	respBody := prepareUploadStreams(context, buf)
	if err = uploadFiles(context, false, request, buf, body, values); err != nil {
		return err
	}
	resp, err := client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return cmd.ErrAbortCommand
	}
	_, err = io.Copy(respBody, resp.Body)
	if err != nil {
		return err
	}
	if strings.HasSuffix(buf.String(), "\nOK\n") {
		return nil
	}
	return cmd.ErrAbortCommand
}

func uploadFiles(context *cmd.Context, filesOnly bool, request *http.Request, buf *safe.Buffer, body *safe.Buffer, values url.Values) error {
	writer := multipart.NewWriter(body)
	for k := range values {
		writer.WriteField(k, values.Get(k))
	}
	file, err := writer.CreateFormFile("file", "archive.tar.gz")
	if err != nil {
		return err
	}
	tm := newTarMaker(context)
	err = tm.targz(file, filesOnly, context.Args...)
	if err != nil {
		return err
	}
	writer.Close()
	request.Header.Set("Content-Type", "multipart/form-data; boundary="+writer.Boundary())
	fullSize := float64(body.Len())
	megabyte := 1024.0 * 1024.0
	fmt.Fprintf(context.Stdout, "Uploading files (%0.2fMB)... ", fullSize/megabyte)
	count := 0
	go func() {
		t0 := time.Now()
		lastTransferred := 0.0
		for buf.Len() == 0 {
			remaining := body.Len()
			transferred := fullSize - float64(remaining)
			speed := ((transferred - lastTransferred) / megabyte) / (float64(time.Since(t0)) / float64(time.Second))
			t0 = time.Now()
			lastTransferred = transferred
			percent := (transferred / fullSize) * 100.0
			fmt.Fprintf(context.Stdout, "\rUploading files (%0.2fMB)... %0.2f%%", fullSize/megabyte, percent)
			if remaining > 0 {
				fmt.Fprintf(context.Stdout, " (%0.2fMB/s)", speed)
			}
			if remaining == 0 && buf.Len() == 0 {
				fmt.Fprintf(context.Stdout, " Processing%s", strings.Repeat(".", count))
				count++
			}
			time.Sleep(2e9)
		}
	}()
	return nil
}
<commit_msg>client/build: add file-only tag<commit_after>package client

import (
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/tsuru/gnuflag"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/safe"
)

type AppBuild struct {
	cmd.GuessingCommand
	tag       string
	fs        *gnuflag.FlagSet
	filesOnly bool
}

func (c *AppBuild) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.GuessingCommand.Flags()
		tag := "The image tag"
		c.fs.StringVar(&c.tag, "tag", "", tag)
		c.fs.StringVar(&c.tag, "t", "", tag)
		filesOnly := "Enables single file build into the root of the app's tree"
		c.fs.BoolVar(&c.filesOnly, "f", false, filesOnly)
		c.fs.BoolVar(&c.filesOnly, "files-only", false, filesOnly)
	}
	return c.fs
}

func (c *AppBuild) Info() *cmd.Info {
	desc := `Builds a tsuru app image respecting .tsuruignore file. Some examples of calls are:

::

		$ tsuru app-build -a myapp -t mytag .
		$ tsuru app-build -a myapp -t latest myfile.jar Procfile
		$ tsuru app-build -a myapp -t mytag -f directory/main.go directory/Procfile
`
	return &cmd.Info{
		Name:    "app-build",
		Usage:   "app-build [-a/--app <appname>] [-t/--tag <image_tag>] [-f/--files-only] <file-or-dir-1> [file-or-dir-2] ... [file-or-dir-n]",
		Desc:    desc,
		MinArgs: 0,
	}
}

func (c *AppBuild) Run(context *cmd.Context, client *cmd.Client) error {
	context.RawOutput()
	if c.tag == "" {
		return errors.New("You should provide one tag to build the image.\n")
	}
	if len(context.Args) == 0 {
		return errors.New("You should provide at least one file to build the image.\n")
	}
	appName, err := c.Guess()
	if err != nil {
		return err
	}
	u, err := cmd.GetURL("/apps/" + appName)
	if err != nil {
		return err
	}
	request, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	if err != nil {
		return err
	}
	values := url.Values{}
	values.Set("tag", c.tag)
	u, err = cmd.GetURLVersion("1.5", fmt.Sprintf("/apps/%s/build", appName))
	if err != nil {
		return err
	}
	body := safe.NewBuffer(nil)
	request, err = http.NewRequest("POST", u, body)
	if err != nil {
		return err
	}
	buf := safe.NewBuffer(nil)
	respBody := prepareUploadStreams(context, buf)
	if err = uploadFiles(context, c.filesOnly, request, buf, body, values); err != nil {
		return err
	}
	resp, err := client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return cmd.ErrAbortCommand
	}
	_, err = io.Copy(respBody, resp.Body)
	if err != nil {
		return err
	}
	if strings.HasSuffix(buf.String(), "\nOK\n") {
		return nil
	}
	return cmd.ErrAbortCommand
}

func uploadFiles(context *cmd.Context, filesOnly bool, request *http.Request, buf *safe.Buffer, body *safe.Buffer, values url.Values) error {
	writer := multipart.NewWriter(body)
	for k := range values {
		writer.WriteField(k, values.Get(k))
	}
	file, err := writer.CreateFormFile("file", "archive.tar.gz")
	if err != nil {
		return err
	}
	tm := newTarMaker(context)
	err = tm.targz(file, filesOnly, context.Args...)
	if err != nil {
		return err
	}
	writer.Close()
	request.Header.Set("Content-Type", "multipart/form-data; boundary="+writer.Boundary())
	fullSize := float64(body.Len())
	megabyte := 1024.0 * 1024.0
	fmt.Fprintf(context.Stdout, "Uploading files (%0.2fMB)... ", fullSize/megabyte)
	count := 0
	go func() {
		t0 := time.Now()
		lastTransferred := 0.0
		for buf.Len() == 0 {
			remaining := body.Len()
			transferred := fullSize - float64(remaining)
			speed := ((transferred - lastTransferred) / megabyte) / (float64(time.Since(t0)) / float64(time.Second))
			t0 = time.Now()
			lastTransferred = transferred
			percent := (transferred / fullSize) * 100.0
			fmt.Fprintf(context.Stdout, "\rUploading files (%0.2fMB)... %0.2f%%", fullSize/megabyte, percent)
			if remaining > 0 {
				fmt.Fprintf(context.Stdout, " (%0.2fMB/s)", speed)
			}
			if remaining == 0 && buf.Len() == 0 {
				fmt.Fprintf(context.Stdout, " Processing%s", strings.Repeat(".", count))
				count++
			}
			time.Sleep(2e9)
		}
	}()
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"

	yaml "gopkg.in/yaml.v2"

	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/apex/log"
	"github.com/spf13/viper"
)

const (
	appFilename = "app"
	euiKey      = "eui"
	idKey       = "id"
)

// GetConfigFile returns the location of the configuration file.
// It checks the following (in this order):
// the --config flag
// $XDG_CONFIG_HOME/ttnctl/config.yml (if $XDG_CONFIG_HOME is set)
// $HOME/.ttnctl.yml
func GetConfigFile() string {
	flag := viper.GetString("config")

	xdg := os.Getenv("XDG_CONFIG_HOME")
	if xdg != "" {
		xdg = path.Join(xdg, "ttnctl", "config.yml")
	}

	home := os.Getenv("HOME")
	if home != "" {
		home = path.Join(home, ".ttnctl.yml")
	}

	for _, file := range []string{
		flag,
		xdg,
		home,
	} {
		if file != "" {
			if _, err := os.Stat(file); err == nil {
				return file
			}
		}
	}

	return ""
}

// GetDataDir returns the location of the data directory used for
// sotring data.
// It checks the following (in this order):
// the --data flag
// $XDG_DATA_HOME/ttnctl (if $XDG_DATA_HOME is set)
// $XDG_CACHE_HOME/ttnctl (if $XDG_CACHE_HOME is set)
// $HOME/.ttnctl
func GetDataDir() string {
	file := viper.GetString("data")
	if file != "" {
		return file
	}

	xdg := os.Getenv("XDG_DATA_HOME")
	if xdg != "" {
		return path.Join(xdg, "ttnctl")
	}

	xdg = os.Getenv("XDG_CACHE_HOME")
	if xdg != "" {
		return path.Join(xdg, "ttnctl")
	}

	return path.Join(os.Getenv("HOME"), ".ttnctl")
}

func readData(file string) map[string]interface{} {
	fullpath := path.Join(GetDataDir(), file)

	c := make(map[string]interface{})

	// Read config file
	data, err := ioutil.ReadFile(fullpath)
	if err != nil {
		return c
	}

	err = yaml.Unmarshal(data, &c)
	if err != nil {
		return c
	}

	return c
}

func writeData(file string, data map[string]interface{}) error {
	fullpath := path.Join(GetDataDir(), file)

	// Generate yaml contents
	d, err := yaml.Marshal(&data)
	if err != nil {
		return fmt.Errorf("Could not generate configiguration file contents: %s", err.Error())
	}

	// Write to file
	err = ioutil.WriteFile(fullpath, d, 0644)
	if err != nil {
		return fmt.Errorf("Could not write configuration file: %s", err.Error())
	}

	return nil
}

func setData(file, key string, data interface{}) error {
	config := readData(file)
	config[key] = data
	return writeData(file, config)
}

// GetAppEUI returns the AppEUI that must be set in the command options or config
func GetAppEUI(ctx log.Interface) types.AppEUI {
	appEUIString := viper.GetString("app-eui")
	if appEUIString == "" {
		appData := readData(appFilename)
		eui, ok := appData[euiKey].(string)
		if !ok {
			ctx.Fatal("Invalid AppEUI in config file")
		}
		appEUIString = eui
	}

	if appEUIString == "" {
		ctx.Fatal("Missing AppEUI. You should select an application to use with \"ttnctl applications select\"")
	}

	eui, err := types.ParseAppEUI(appEUIString)
	if err != nil {
		ctx.WithError(err).Fatal("Invalid AppEUI")
	}
	return eui
}

// SetApp stores the app EUI preference
func SetAppEUI(ctx log.Interface, appEUI types.AppEUI) {
	err := setData(appFilename, euiKey, appEUI.String())
	if err != nil {
		ctx.WithError(err).Fatal("Could not save app EUI")
	}
}

// GetAppID returns the AppID that must be set in the command options or config
func GetAppID(ctx log.Interface) string {
	appID := viper.GetString("app-id")
	if appID == "" {
		appData := readData(appFilename)
		id, ok := appData[idKey].(string)
		if !ok {
			ctx.Fatal("Invalid appID in config file.")
		}
		appID = id
	}

	if appID == "" {
		ctx.Fatal("Missing AppID. You should select an application to use with \"ttnctl applications select\"")
	}
	return appID
}

// SetApp stores the app ID and app EUI preferences
func SetApp(ctx log.Interface, appID string, appEUI types.AppEUI) {
	config := readData(appFilename)
	config[idKey] = appID
	config[euiKey] = appEUI.String()
	err := writeData(appFilename, config)
	if err != nil {
		ctx.WithError(err).Fatal("Could not save app preference")
	}
}
<commit_msg>Return correct config file, even if none were found<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"

	yaml "gopkg.in/yaml.v2"

	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/apex/log"
	"github.com/spf13/viper"
)

const (
	appFilename = "app"
	euiKey      = "eui"
	idKey       = "id"
)

// GetConfigFile returns the location of the configuration file.
// It checks the following (in this order):
// the --config flag
// $XDG_CONFIG_HOME/ttnctl/config.yml (if $XDG_CONFIG_HOME is set)
// $HOME/.ttnctl.yml
func GetConfigFile() string {
	flag := viper.GetString("config")
	xdg := path.Join(os.Getenv("XDG_CONFIG_HOME"), "ttnctl", "config.yml")
	homeyml := path.Join(os.Getenv("HOME"), ".ttnctl.yml")
	homeyaml := path.Join(os.Getenv("HOME"), ".ttnctl.yaml")

	try_files := []string{
		flag,
		xdg,
		homeyml,
		homeyaml,
	}

	// find a file that exists, and use that
	for _, file := range try_files {
		if file != "" {
			if _, err := os.Stat(file); err == nil {
				return file
			}
		}
	}

	// no file found, set up correct fallback
	if os.Getenv("XDG_CONFIG_HOME") != "" {
		return xdg
	} else {
		return homeyml
	}
}

// GetDataDir returns the location of the data directory used for
// sotring data.
// It checks the following (in this order):
// the --data flag
// $XDG_DATA_HOME/ttnctl (if $XDG_DATA_HOME is set)
// $XDG_CACHE_HOME/ttnctl (if $XDG_CACHE_HOME is set)
// $HOME/.ttnctl
func GetDataDir() string {
	file := viper.GetString("data")
	if file != "" {
		return file
	}

	xdg := os.Getenv("XDG_DATA_HOME")
	if xdg != "" {
		return path.Join(xdg, "ttnctl")
	}

	xdg = os.Getenv("XDG_CACHE_HOME")
	if xdg != "" {
		return path.Join(xdg, "ttnctl")
	}

	return path.Join(os.Getenv("HOME"), ".ttnctl")
}

func readData(file string) map[string]interface{} {
	fullpath := path.Join(GetDataDir(), file)

	c := make(map[string]interface{})

	// Read config file
	data, err := ioutil.ReadFile(fullpath)
	if err != nil {
		return c
	}

	err = yaml.Unmarshal(data, &c)
	if err != nil {
		return c
	}

	return c
}

func writeData(file string, data map[string]interface{}) error {
	fullpath := path.Join(GetDataDir(), file)

	// Generate yaml contents
	d, err := yaml.Marshal(&data)
	if err != nil {
		return fmt.Errorf("Could not generate configiguration file contents: %s", err.Error())
	}

	// Write to file
	err = ioutil.WriteFile(fullpath, d, 0644)
	if err != nil {
		return fmt.Errorf("Could not write configuration file: %s", err.Error())
	}

	return nil
}

func setData(file, key string, data interface{}) error {
	config := readData(file)
	config[key] = data
	return writeData(file, config)
}

// GetAppEUI returns the AppEUI that must be set in the command options or config
func GetAppEUI(ctx log.Interface) types.AppEUI {
	appEUIString := viper.GetString("app-eui")
	if appEUIString == "" {
		appData := readData(appFilename)
		eui, ok := appData[euiKey].(string)
		if !ok {
			ctx.Fatal("Invalid AppEUI in config file")
		}
		appEUIString = eui
	}

	if appEUIString == "" {
		ctx.Fatal("Missing AppEUI. You should select an application to use with \"ttnctl applications select\"")
	}

	eui, err := types.ParseAppEUI(appEUIString)
	if err != nil {
		ctx.WithError(err).Fatal("Invalid AppEUI")
	}
	return eui
}

// SetApp stores the app EUI preference
func SetAppEUI(ctx log.Interface, appEUI types.AppEUI) {
	err := setData(appFilename, euiKey, appEUI.String())
	if err != nil {
		ctx.WithError(err).Fatal("Could not save app EUI")
	}
}

// GetAppID returns the AppID that must be set in the command options or config
func GetAppID(ctx log.Interface) string {
	appID := viper.GetString("app-id")
	if appID == "" {
		appData := readData(appFilename)
		id, ok := appData[idKey].(string)
		if !ok {
			ctx.Fatal("Invalid appID in config file.")
		}
		appID = id
	}

	if appID == "" {
		ctx.Fatal("Missing AppID. You should select an application to use with \"ttnctl applications select\"")
	}
	return appID
}

// SetApp stores the app ID and app EUI preferences
func SetApp(ctx log.Interface, appID string, appEUI types.AppEUI) {
	config := readData(appFilename)
	config[idKey] = appID
	config[euiKey] = appEUI.String()
	err := writeData(appFilename, config)
	if err != nil {
		ctx.WithError(err).Fatal("Could not save app preference")
	}
}
<|endoftext|>


<commit_before>package fuse

import (
	"log"
	"os"
	"strings"
	"time"
	"unsafe"
)

const (
	// The kernel caps writes at 128k.
	MAX_KERNEL_WRITE = 128 * 1024
)

// MountState contains the logic for reading from the FUSE device and
// translating it to RawFileSystem interface calls.
type MountState struct {
	// Empty if unmounted.
	mountPoint string
	fileSystem RawFileSystem

	// I/O with kernel and daemon.
	mountFile *os.File

	// Dump debug info onto stdout.
	Debug bool

	// For efficient reads and writes.
	buffers *BufferPoolImpl

	latencies *LatencyMap

	opts           *MountOptions
	kernelSettings InitIn
}

func (me *MountState) KernelSettings() InitIn {
	return me.kernelSettings
}

func (me *MountState) MountPoint() string {
	return me.mountPoint
}

// Mount filesystem on mountPoint.
func (me *MountState) Mount(mountPoint string, opts *MountOptions) error {
	if opts == nil {
		opts = &MountOptions{
			MaxBackground: _DEFAULT_BACKGROUND_TASKS,
		}
	}
	o := *opts
	if o.MaxWrite < 0 {
		o.MaxWrite = 0
	}
	if o.MaxWrite == 0 {
		o.MaxWrite = 1 << 16
	}
	if o.MaxWrite > MAX_KERNEL_WRITE {
		o.MaxWrite = MAX_KERNEL_WRITE
	}
	opts = &o
	me.opts = &o

	optStrs := opts.Options
	if opts.AllowOther {
		optStrs = append(optStrs, "allow_other")
	}

	file, mp, err := mount(mountPoint, strings.Join(optStrs, ","))
	if err != nil {
		return err
	}
	initParams := RawFsInit{
		InodeNotify: func(n *NotifyInvalInodeOut) Status {
			return me.writeInodeNotify(n)
		},
		EntryNotify: func(parent uint64, n string) Status {
			return me.writeEntryNotify(parent, n)
		},
	}
	me.fileSystem.Init(&initParams)
	me.mountPoint = mp
	me.mountFile = file
	return nil
}

func (me *MountState) SetRecordStatistics(record bool) {
	if record {
		me.latencies = NewLatencyMap()
	} else {
		me.latencies = nil
	}
}

func (me *MountState) Unmount() (err error) {
	delay := int64(0)
	for try := 0; try < 3; try++ {
		err = unmount(me.mountPoint)
		if err == nil {
			break
		}

		// Sleep for a bit. This is not pretty, but there is
		// no way we can be certain that the kernel thinks all
		// open files have already been closed.
		delay = 2*delay + 1e6
		time.Sleep(delay)
	}
	if err == nil {
		me.mountPoint = ""
		me.mountFile.Close()
		me.mountFile = nil
	}
	return err
}

func NewMountState(fs RawFileSystem) *MountState {
	me := new(MountState)
	me.mountPoint = ""
	me.fileSystem = fs
	me.buffers = NewBufferPool()
	return me
}

func (me *MountState) Latencies() map[string]float64 {
	return me.latencies.Latencies(1e-3)
}

func (me *MountState) OperationCounts() map[string]int {
	return me.latencies.Counts()
}

func (me *MountState) BufferPoolStats() string {
	return me.buffers.String()
}

func (me *MountState) newRequest() *request {
	return &request{
		status:   OK,
		inputBuf: me.buffers.AllocBuffer(uint32(me.opts.MaxWrite + 4096)),
	}
}

func (me *MountState) readRequest(req *request) Status {
	n, err := me.mountFile.Read(req.inputBuf)
	// If we start timing before the read, we may take into
	// account waiting for input into the timing.
	if me.latencies != nil {
		req.startNs = time.Nanoseconds()
	}
	req.inputBuf = req.inputBuf[0:n]
	return ToStatus(err)
}

func (me *MountState) recordStats(req *request) {
	if me.latencies != nil {
		endNs := time.Nanoseconds()
		dt := endNs - req.startNs

		opname := operationName(req.inHeader.opcode)
		me.latencies.AddMany(
			[]LatencyArg{
				{opname, "", dt},
				{opname + "-write", "", endNs - req.preWriteNs}})
	}
}

// Loop initiates the FUSE loop. Normally, callers should run Loop()
// and wait for it to exit, but tests will want to run this in a
// goroutine.
//
// Each filesystem operation executes in a separate goroutine.
func (me *MountState) Loop() {
	me.loop()
	me.mountFile.Close()
}

func (me *MountState) loop() {
	for {
		req := me.newRequest()
		errNo := me.readRequest(req)
		if !errNo.Ok() {
			// Retry.
			if errNo == ENOENT {
				continue
			}

			if errNo == ENODEV {
				// Unmount.
				break
			}

			log.Printf("Failed to read from fuse conn: %v", errNo)
			break
		}

		// When closely analyzing timings, the context switch
		// generates some delay.  While unfortunate, the
		// alternative is to have a fixed goroutine pool,
		// which will lock up the FS if the daemon has too
		// many blocking calls.
		go func(r *request) {
			me.handleRequest(r)
			me.discardRequest(r)
		}(req)
	}
}

func (me *MountState) discardRequest(req *request) {
	me.buffers.FreeBuffer(req.flatData)
	me.buffers.FreeBuffer(req.inputBuf)
}

func (me *MountState) handleRequest(req *request) {
	defer me.recordStats(req)

	req.parse()
	if req.handler == nil {
		req.status = ENOSYS
	}

	if req.status.Ok() && me.Debug {
		log.Println(req.InputDebug())
	}

	if req.status.Ok() && req.handler.Func == nil {
		log.Printf("Unimplemented opcode %v", req.inHeader.opcode)
		req.status = ENOSYS
	}

	if req.status.Ok() {
		req.handler.Func(me, req)
	}

	errNo := me.write(req)
	if errNo != 0 {
		log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v",
			req.outHeaderBytes, errNo, operationName(req.inHeader.opcode))
	}
}

func (me *MountState) write(req *request) Status {
	// If we try to write OK, nil, we will get
	// error:  writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
	// failed, err: writev: no such file or directory
	if req.inHeader.opcode == _OP_FORGET {
		return OK
	}

	req.serialize()
	if me.Debug {
		log.Println(req.OutputDebug())
	}

	if me.latencies != nil {
		req.preWriteNs = time.Nanoseconds()
	}

	if req.outHeaderBytes == nil {
		return OK
	}

	var err error
	if req.flatData == nil {
		_, err = me.mountFile.Write(req.outHeaderBytes)
	} else {
		_, err = Writev(me.mountFile.Fd(),
			[][]byte{req.outHeaderBytes, req.flatData})
	}

	return ToStatus(err)
}

func (me *MountState) writeInodeNotify(entry *NotifyInvalInodeOut) Status {
	req := request{
		inHeader: &InHeader{
			opcode: _OP_NOTIFY_INODE,
		},
		handler: operationHandlers[_OP_NOTIFY_INODE],
		status:  NOTIFY_INVAL_INODE,
	}
	req.outData = unsafe.Pointer(entry)
	req.serialize()
	result := me.write(&req)

	if me.Debug {
		log.Println("Response: INODE_NOTIFY", result)
	}
	return result
}

func (me *MountState) writeEntryNotify(parent uint64, name string) Status {
	req := request{
		inHeader: &InHeader{
			opcode: _OP_NOTIFY_ENTRY,
		},
		handler: operationHandlers[_OP_NOTIFY_ENTRY],
		status:  NOTIFY_INVAL_ENTRY,
	}
	entry := &NotifyInvalEntryOut{
		Parent:  parent,
		NameLen: uint32(len(name)),
	}

	// Many versions of FUSE generate stacktraces if the
	// terminating null byte is missing.
	nameBytes := []byte(name + "\000")
	req.outData = unsafe.Pointer(entry)
	req.flatData = nameBytes
	req.serialize()
	result := me.write(&req)

	if me.Debug {
		log.Printf("Response: ENTRY_NOTIFY: %v", result)
	}
	return result
}
<commit_msg>Don't try to unmount if we are already unmounted.<commit_after>package fuse

import (
	"log"
	"os"
	"strings"
	"time"
	"unsafe"
)

const (
	// The kernel caps writes at 128k.
	MAX_KERNEL_WRITE = 128 * 1024
)

// MountState contains the logic for reading from the FUSE device and
// translating it to RawFileSystem interface calls.
type MountState struct {
	// Empty if unmounted.
	mountPoint string
	fileSystem RawFileSystem

	// I/O with kernel and daemon.
	mountFile *os.File

	// Dump debug info onto stdout.
	Debug bool

	// For efficient reads and writes.
	buffers *BufferPoolImpl

	latencies *LatencyMap

	opts           *MountOptions
	kernelSettings InitIn
}

func (me *MountState) KernelSettings() InitIn {
	return me.kernelSettings
}

func (me *MountState) MountPoint() string {
	return me.mountPoint
}

// Mount filesystem on mountPoint.
func (me *MountState) Mount(mountPoint string, opts *MountOptions) error {
	if opts == nil {
		opts = &MountOptions{
			MaxBackground: _DEFAULT_BACKGROUND_TASKS,
		}
	}
	o := *opts
	if o.MaxWrite < 0 {
		o.MaxWrite = 0
	}
	if o.MaxWrite == 0 {
		o.MaxWrite = 1 << 16
	}
	if o.MaxWrite > MAX_KERNEL_WRITE {
		o.MaxWrite = MAX_KERNEL_WRITE
	}
	opts = &o
	me.opts = &o

	optStrs := opts.Options
	if opts.AllowOther {
		optStrs = append(optStrs, "allow_other")
	}

	file, mp, err := mount(mountPoint, strings.Join(optStrs, ","))
	if err != nil {
		return err
	}
	initParams := RawFsInit{
		InodeNotify: func(n *NotifyInvalInodeOut) Status {
			return me.writeInodeNotify(n)
		},
		EntryNotify: func(parent uint64, n string) Status {
			return me.writeEntryNotify(parent, n)
		},
	}
	me.fileSystem.Init(&initParams)
	me.mountPoint = mp
	me.mountFile = file
	return nil
}

func (me *MountState) SetRecordStatistics(record bool) {
	if record {
		me.latencies = NewLatencyMap()
	} else {
		me.latencies = nil
	}
}

func (me *MountState) Unmount() (err error) {
	if me.mountPoint == "" {
		return nil
	}
	delay := int64(0)
	for try := 0; try < 3; try++ {
		err = unmount(me.mountPoint)
		if err == nil {
			break
		}

		// Sleep for a bit. This is not pretty, but there is
		// no way we can be certain that the kernel thinks all
		// open files have already been closed.
		delay = 2*delay + 1e6
		time.Sleep(delay)
	}
	if err == nil {
		me.mountPoint = ""
		me.mountFile.Close()
		me.mountFile = nil
	}
	return err
}

func NewMountState(fs RawFileSystem) *MountState {
	me := new(MountState)
	me.mountPoint = ""
	me.fileSystem = fs
	me.buffers = NewBufferPool()
	return me
}

func (me *MountState) Latencies() map[string]float64 {
	return me.latencies.Latencies(1e-3)
}

func (me *MountState) OperationCounts() map[string]int {
	return me.latencies.Counts()
}

func (me *MountState) BufferPoolStats() string {
	return me.buffers.String()
}

func (me *MountState) newRequest() *request {
	return &request{
		status:   OK,
		inputBuf: me.buffers.AllocBuffer(uint32(me.opts.MaxWrite + 4096)),
	}
}

func (me *MountState) readRequest(req *request) Status {
	n, err := me.mountFile.Read(req.inputBuf)
	// If we start timing before the read, we may take into
	// account waiting for input into the timing.
	if me.latencies != nil {
		req.startNs = time.Nanoseconds()
	}
	req.inputBuf = req.inputBuf[0:n]
	return ToStatus(err)
}

func (me *MountState) recordStats(req *request) {
	if me.latencies != nil {
		endNs := time.Nanoseconds()
		dt := endNs - req.startNs

		opname := operationName(req.inHeader.opcode)
		me.latencies.AddMany(
			[]LatencyArg{
				{opname, "", dt},
				{opname + "-write", "", endNs - req.preWriteNs}})
	}
}

// Loop initiates the FUSE loop. Normally, callers should run Loop()
// and wait for it to exit, but tests will want to run this in a
// goroutine.
//
// Each filesystem operation executes in a separate goroutine.
func (me *MountState) Loop() {
	me.loop()
	me.mountFile.Close()
}

func (me *MountState) loop() {
	for {
		req := me.newRequest()
		errNo := me.readRequest(req)
		if !errNo.Ok() {
			// Retry.
			if errNo == ENOENT {
				continue
			}

			if errNo == ENODEV {
				// Unmount.
				break
			}

			log.Printf("Failed to read from fuse conn: %v", errNo)
			break
		}

		// When closely analyzing timings, the context switch
		// generates some delay.  While unfortunate, the
		// alternative is to have a fixed goroutine pool,
		// which will lock up the FS if the daemon has too
		// many blocking calls.
		go func(r *request) {
			me.handleRequest(r)
			me.discardRequest(r)
		}(req)
	}
}

func (me *MountState) discardRequest(req *request) {
	me.buffers.FreeBuffer(req.flatData)
	me.buffers.FreeBuffer(req.inputBuf)
}

func (me *MountState) handleRequest(req *request) {
	defer me.recordStats(req)

	req.parse()
	if req.handler == nil {
		req.status = ENOSYS
	}

	if req.status.Ok() && me.Debug {
		log.Println(req.InputDebug())
	}

	if req.status.Ok() && req.handler.Func == nil {
		log.Printf("Unimplemented opcode %v", req.inHeader.opcode)
		req.status = ENOSYS
	}

	if req.status.Ok() {
		req.handler.Func(me, req)
	}

	errNo := me.write(req)
	if errNo != 0 {
		log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v",
			req.outHeaderBytes, errNo, operationName(req.inHeader.opcode))
	}
}

func (me *MountState) write(req *request) Status {
	// If we try to write OK, nil, we will get
	// error:  writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
	// failed, err: writev: no such file or directory
	if req.inHeader.opcode == _OP_FORGET {
		return OK
	}

	req.serialize()
	if me.Debug {
		log.Println(req.OutputDebug())
	}

	if me.latencies != nil {
		req.preWriteNs = time.Nanoseconds()
	}

	if req.outHeaderBytes == nil {
		return OK
	}

	var err error
	if req.flatData == nil {
		_, err = me.mountFile.Write(req.outHeaderBytes)
	} else {
		_, err = Writev(me.mountFile.Fd(),
			[][]byte{req.outHeaderBytes, req.flatData})
	}

	return ToStatus(err)
}

func (me *MountState) writeInodeNotify(entry *NotifyInvalInodeOut) Status {
	req := request{
		inHeader: &InHeader{
			opcode: _OP_NOTIFY_INODE,
		},
		handler: operationHandlers[_OP_NOTIFY_INODE],
		status:  NOTIFY_INVAL_INODE,
	}
	req.outData = unsafe.Pointer(entry)
	req.serialize()
	result := me.write(&req)

	if me.Debug {
		log.Println("Response: INODE_NOTIFY", result)
	}
	return result
}

func (me *MountState) writeEntryNotify(parent uint64, name string) Status {
	req := request{
		inHeader: &InHeader{
			opcode: _OP_NOTIFY_ENTRY,
		},
		handler: operationHandlers[_OP_NOTIFY_ENTRY],
		status:  NOTIFY_INVAL_ENTRY,
	}
	entry := &NotifyInvalEntryOut{
		Parent:  parent,
		NameLen: uint32(len(name)),
	}

	// Many versions of FUSE generate stacktraces if the
	// terminating null byte is missing.
	nameBytes := []byte(name + "\000")
	req.outData = unsafe.Pointer(entry)
	req.flatData = nameBytes
	req.serialize()
	result := me.write(&req)

	if me.Debug {
		log.Printf("Response: ENTRY_NOTIFY: %v", result)
	}
	return result
}
<|endoftext|>


<commit_before>package rds

import (
	"github.com/jagregory/cfval/constraints"
	"github.com/jagregory/cfval/resources/common"
	. "github.com/jagregory/cfval/schema"
)

// see: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnet-group.html
var DBSubnetGroup = Resource{
	AwsType: "AWS::RDS::DBSubnetGroup",

	Properties: map[string]Schema{
		"DBSubnetGroupDescription": Schema{
			Type:     ValueString,
			Required: constraints.Always,
		},

		"SubnetIds": Schema{
			Type:     Multiple(SubnetID),
			Required: constraints.Always,
		},

		"Tags": Schema{
			Type: Multiple(common.ResourceTag),
		},
	},
}
<commit_msg>Set ReturnValue for DBSubnetGroup after docs were updated<commit_after>package rds

import (
	"github.com/jagregory/cfval/constraints"
	"github.com/jagregory/cfval/resources/common"
	. "github.com/jagregory/cfval/schema"
)

// see: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnet-group.html
var DBSubnetGroup = Resource{
	AwsType: "AWS::RDS::DBSubnetGroup",

	// Name
	ReturnValue: Schema{
		Type: ValueString,
	},

	Properties: map[string]Schema{
		"DBSubnetGroupDescription": Schema{
			Type:     ValueString,
			Required: constraints.Always,
		},

		"SubnetIds": Schema{
			Type:     Multiple(SubnetID),
			Required: constraints.Always,
		},

		"Tags": Schema{
			Type: Multiple(common.ResourceTag),
		},
	},
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

package fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpCreate:
		size := fusekernel.CreateInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.CreateIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &CreateFileOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSymlink:
		// m.Bytes() is "newName\0target\0"
		names := m.Bytes()
		if len(names) == 0 || names[len(names)-1] != 0 {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		newName, target := names[0:i], names[i+1:len(names)-1]

		to := &CreateSymlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(newName),
			Target: string(target),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRename:
		in := (*fusekernel.RenameIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}
		names := m.Bytes()[unsafe.Sizeof(*in):]
		// names should be "old\x00new\x00"
		if len(names) < 4 {
			goto corrupt
		}
		if names[len(names)-1] != '\x00' {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		oldName, newName := names[:i], names[i+1:len(names)-1]

		to := &RenameOp{
			OldParent: InodeID(m.Header().Node),
			OldName:   string(oldName),
			NewParent: InodeID(in.Newdir),
			NewName:   string(newName),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpUnlink:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &UnlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRmdir:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &RmDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<commit_msg>ReadFileOp<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

package fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpCreate:
		size := fusekernel.CreateInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.CreateIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &CreateFileOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSymlink:
		// m.Bytes() is "newName\0target\0"
		names := m.Bytes()
		if len(names) == 0 || names[len(names)-1] != 0 {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		newName, target := names[0:i], names[i+1:len(names)-1]

		to := &CreateSymlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(newName),
			Target: string(target),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRename:
		in := (*fusekernel.RenameIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}
		names := m.Bytes()[unsafe.Sizeof(*in):]
		// names should be "old\x00new\x00"
		if len(names) < 4 {
			goto corrupt
		}
		if names[len(names)-1] != '\x00' {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		oldName, newName := names[:i], names[i+1:len(names)-1]

		to := &RenameOp{
			OldParent: InodeID(m.Header().Node),
			OldName:   string(oldName),
			NewParent: InodeID(in.Newdir),
			NewName:   string(newName),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpUnlink:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &UnlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRmdir:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &RmDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case fusekernel.OpRead:
		in := (*fusekernel.ReadIn)(m.Data())
		if m.Len() < fusekernel.ReadInSize(protocol) {
			goto corrupt
		}

		to := &ReadFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Offset: int64(in.Offset),
			Size:   int(in.Size),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

package fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpCreate:
		size := fusekernel.CreateInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.CreateIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &CreateFileOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSymlink:
		// m.Bytes() is "newName\0target\0"
		names := m.Bytes()
		if len(names) == 0 || names[len(names)-1] != 0 {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		newName, target := names[0:i], names[i+1:len(names)-1]

		to := &CreateSymlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(newName),
			Target: string(target),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRename:
		in := (*fusekernel.RenameIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}
		names := m.Bytes()[unsafe.Sizeof(*in):]
		// names should be "old\x00new\x00"
		if len(names) < 4 {
			goto corrupt
		}
		if names[len(names)-1] != '\x00' {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		oldName, newName := names[:i], names[i+1:len(names)-1]

		to := &RenameOp{
			OldParent: InodeID(m.Header().Node),
			OldName:   string(oldName),
			NewParent: InodeID(in.Newdir),
			NewName:   string(newName),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpUnlink:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &UnlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRmdir:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &RmDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpOpen:
		to := &OpenFileOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpOpendir:
		to := &OpenDirOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRead:
		in := (*fusekernel.ReadIn)(m.Data())
		if m.Len() < fusekernel.ReadInSize(protocol) {
			goto corrupt
		}

		to := &ReadFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Offset: int64(in.Offset),
			Size:   int(in.Size),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReaddir:
		in := (*fusekernel.ReadIn)(m.Data())
		if m.Len() < fusekernel.ReadInSize(protocol) {
			goto corrupt
		}

		to := &ReadDirOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Offset: DirOffset(in.Offset),
			Size:   int(in.Size),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRelease:
		in := (*fusekernel.ReleaseIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ReleaseFileHandleOp{
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReleasedir:
		in := (*fusekernel.ReleaseIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ReleaseDirHandleOp{
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpWrite:
		in := (*fusekernel.WriteIn)(m.Data())
		size := fusekernel.WriteInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}

		buf := m.Bytes()[size:]
		if len(buf) < int(in.Size) {
			goto corrupt
		}

		to := &WriteFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Data:   buf,
			Offset: int64(in.Offset),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpFsync:
		in := (*fusekernel.FsyncIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SyncFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpFlush:
		in := (*fusekernel.FlushIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &FlushFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReadlink:
		to := &ReadSymlinkOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<commit_msg>Fixed an error.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

package fuseops

import (
	"bytes"
	"log"
	"time"
	"unsafe"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	protocol fusekernel.Protocol,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSetattr:
		in := (*fusekernel.SetattrIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}

		valid := fusekernel.SetattrValid(in.Valid)
		if valid&fusekernel.SetattrSize != 0 {
			to.Size = &in.Size
		}

		if valid&fusekernel.SetattrMode != 0 {
			mode := fuseshim.FileMode(in.Mode)
			to.Mode = &mode
		}

		if valid&fusekernel.SetattrAtime != 0 {
			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
			to.Atime = &t
		}

		if valid&fusekernel.SetattrMtime != 0 {
			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
			to.Mtime = &t
		}

		io = to
		co = &to.commonOp

	case fusekernel.OpForget:
		in := (*fusekernel.ForgetIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ForgetInodeOp{
			Inode: InodeID(m.Header().Node),
			N:     in.Nlookup,
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpMkdir:
		size := fusekernel.MkdirInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.MkdirIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &MkDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpCreate:
		size := fusekernel.CreateInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}
		in := (*fusekernel.CreateIn)(m.Data())
		name := m.Bytes()[size:]
		i := bytes.IndexByte(name, '\x00')
		if i < 0 {
			goto corrupt
		}
		name = name[:i]

		to := &CreateFileOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(name),
			Mode:   fuseshim.FileMode(in.Mode),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpSymlink:
		// m.Bytes() is "newName\0target\0"
		names := m.Bytes()
		if len(names) == 0 || names[len(names)-1] != 0 {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		newName, target := names[0:i], names[i+1:len(names)-1]

		to := &CreateSymlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(newName),
			Target: string(target),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRename:
		in := (*fusekernel.RenameIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}
		names := m.Bytes()[unsafe.Sizeof(*in):]
		// names should be "old\x00new\x00"
		if len(names) < 4 {
			goto corrupt
		}
		if names[len(names)-1] != '\x00' {
			goto corrupt
		}
		i := bytes.IndexByte(names, '\x00')
		if i < 0 {
			goto corrupt
		}
		oldName, newName := names[:i], names[i+1:len(names)-1]

		to := &RenameOp{
			OldParent: InodeID(m.Header().Node),
			OldName:   string(oldName),
			NewParent: InodeID(in.Newdir),
			NewName:   string(newName),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpUnlink:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &UnlinkOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRmdir:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &RmDirOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpOpen:
		to := &OpenFileOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpOpendir:
		to := &OpenDirOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRead:
		in := (*fusekernel.ReadIn)(m.Data())
		if m.Len() < fusekernel.ReadInSize(protocol) {
			goto corrupt
		}

		to := &ReadFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Offset: int64(in.Offset),
			Size:   int(in.Size),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReaddir:
		in := (*fusekernel.ReadIn)(m.Data())
		if m.Len() < fusekernel.ReadInSize(protocol) {
			goto corrupt
		}

		to := &ReadDirOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Offset: DirOffset(in.Offset),
			Size:   int(in.Size),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpRelease:
		in := (*fusekernel.ReleaseIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ReleaseFileHandleOp{
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReleasedir:
		in := (*fusekernel.ReleaseIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &ReleaseDirHandleOp{
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpWrite:
		in := (*fusekernel.WriteIn)(m.Data())
		size := fusekernel.WriteInSize(protocol)
		if m.Len() < size {
			goto corrupt
		}

		buf := m.Bytes()[size:]
		if len(buf) < int(in.Size) {
			goto corrupt
		}

		to := &WriteFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
			Data:   buf,
			Offset: int64(in.Offset),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpFsync:
		in := (*fusekernel.FsyncIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &SyncFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpFlush:
		in := (*fusekernel.FlushIn)(m.Data())
		if m.Len() < unsafe.Sizeof(*in) {
			goto corrupt
		}

		to := &FlushFileOp{
			Inode:  InodeID(m.Header().Node),
			Handle: HandleID(in.Fh),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpReadlink:
		to := &ReadSymlinkOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		m,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package context

import (
	"io"
	"io/ioutil"
	"os"

	"github.com/juju/errors"
	"github.com/juju/juju/resource"
)

// HookContextFacade is the name of the API facade for resources in the uniter.
const HookContextFacade = resource.ComponentName + "-hook-context"

// APIClient exposes the uniter API functionality needed for resources.
type APIClient interface {
	// GetResource returns the resource info and content for the given
	// name (and unit-implied service).
	GetResource(resourceName string) (resource.Resource, io.ReadCloser, error)
}

// Content is the resources portion of a uniter hook context.
type Context struct {
	contextDeps

	apiClient APIClient
	dataDir   string
}

// NewContextAPI returns a new Content for the given API client and data dir.
func NewContextAPI(apiClient APIClient, dataDir string) *Context {
	return &Context{
		contextDeps: newContextDeps(),

		apiClient: apiClient,
		dataDir:   dataDir,
	}
}

// Flush implements jujuc.Context.
func (c *Context) Flush() error {
	return nil
}

// Download downloads the named resource and returns the path
// to which it was downloaded. If the resource does not exist or has
// not been uploaded yet then errors.NotFound is returned.
//
// Note that the downloaded file is checked for correctness.
func (c *Context) Download(name string) (string, error) {
	// TODO(katco): Potential race-condition: two commands running at
	// once.
	// TODO(katco): Check to see if we have latest version

	//resDir := c.resDir(name)
	//current := c.read

	tempDir, err := c.tempDir()
	if err != nil {
		return "", errors.Trace(err)
	}
	defer c.removeDir(tempDir)

	spec := newResourceDirectorySpec(tempDir, name)
	resDir, err := spec.open(c.mkdirAll)
	if err != nil {
		return "", errors.Trace(err)
	}

	info, err := c.downloadResource(name, resDir)
	if err != nil {
		return "", errors.Trace(err)
	}

	resDir, err = c.replaceResourceDir(resDir)
	if err != nil {
		return "", errors.Trace(err)
	}

	resourcePath := resDir.resolve(info.Path)
	return resourcePath, nil
}

// downloadResource downloads the named resource to the provided path.
func (c *Context) downloadResource(name string, resDir *resourceDirectory) (resource.Resource, error) {
	info, resourceReader, err := c.apiClient.GetResource(name)
	if err != nil {
		return resource.Resource{}, errors.Trace(err)
	}
	defer resourceReader.Close()

	if err := resDir.writeInfo(info, c.createFile); err != nil {
		return resource.Resource{}, errors.Trace(err)
	}

	content := resourceContent{
		data:        resourceReader,
		size:        info.Size,
		fingerprint: info.Fingerprint,
	}
	relPath := []string{info.Path}
	if err := resDir.writeResource(relPath, content, c.createFile); err != nil {
		return resource.Resource{}, errors.Trace(err)
	}

	return info, nil
}

// replaceResourceDir replaces the original resource dir
// with the temporary resource dir.
func (c *Context) replaceResourceDir(oldResDir *resourceDirectory) (*resourceDirectory, error) {
	oldDir := oldResDir.resolve()
	newResDir := oldResDir.move(c.dataDir)
	newDir := newResDir.resolve()

	if err := c.replaceDirectory(newDir, oldDir); err != nil {
		return nil, errors.Annotate(err, "could not replace existing resource directory")
	}

	return newResDir, nil
}

type contextDeps struct {
	tempDir          func() (string, error)
	mkdirAll         func(string) error
	removeDir        func(string) error
	replaceDirectory func(string, string) error
	createFile       func(string) (io.WriteCloser, error)
}

func newContextDeps() contextDeps {
	return contextDeps{
		tempDir:          func() (string, error) { return ioutil.TempDir("", "juju-resource-") },
		mkdirAll:         func(path string) error { return os.MkdirAll(path, 0755) },
		removeDir:        os.RemoveAll,
		replaceDirectory: replaceDirectory,
		createFile:       func(path string) (io.WriteCloser, error) { return os.Create(path) },
	}
}

// replaceDirectory replaces the target directory with the source. This
// involves removing the target if it exists and then moving the source
// into place.
func replaceDirectory(targetDir, sourceDir string) error {
	if err := os.RemoveAll(targetDir); err != nil {
		return errors.Trace(err)
	}
	if err := os.Rename(targetDir, sourceDir); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<commit_msg>Add a logger.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package context

import (
	"io"
	"io/ioutil"
	"os"

	"github.com/juju/errors"
	"github.com/juju/loggo"

	"github.com/juju/juju/resource"
)

var logger = loggo.GetLogger("juju.resource.context")

// HookContextFacade is the name of the API facade for resources in the uniter.
const HookContextFacade = resource.ComponentName + "-hook-context"

// APIClient exposes the uniter API functionality needed for resources.
type APIClient interface {
	// GetResource returns the resource info and content for the given
	// name (and unit-implied service).
	GetResource(resourceName string) (resource.Resource, io.ReadCloser, error)
}

// Content is the resources portion of a uniter hook context.
type Context struct {
	contextDeps

	apiClient APIClient
	dataDir   string
}

// NewContextAPI returns a new Content for the given API client and data dir.
func NewContextAPI(apiClient APIClient, dataDir string) *Context {
	return &Context{
		contextDeps: newContextDeps(),

		apiClient: apiClient,
		dataDir:   dataDir,
	}
}

// Flush implements jujuc.Context.
func (c *Context) Flush() error {
	return nil
}

// Download downloads the named resource and returns the path
// to which it was downloaded. If the resource does not exist or has
// not been uploaded yet then errors.NotFound is returned.
//
// Note that the downloaded file is checked for correctness.
func (c *Context) Download(name string) (string, error) {
	// TODO(katco): Potential race-condition: two commands running at
	// once.
	// TODO(katco): Check to see if we have latest version

	//resDir := c.resDir(name)
	//current := c.read

	tempDir, err := c.tempDir()
	if err != nil {
		return "", errors.Trace(err)
	}
	defer c.removeDir(tempDir)

	spec := newResourceDirectorySpec(tempDir, name)
	resDir, err := spec.open(c.mkdirAll)
	if err != nil {
		return "", errors.Trace(err)
	}

	info, err := c.downloadResource(name, resDir)
	if err != nil {
		return "", errors.Trace(err)
	}

	resDir, err = c.replaceResourceDir(resDir)
	if err != nil {
		return "", errors.Trace(err)
	}

	resourcePath := resDir.resolve(info.Path)
	return resourcePath, nil
}

// downloadResource downloads the named resource to the provided path.
func (c *Context) downloadResource(name string, resDir *resourceDirectory) (resource.Resource, error) {
	info, resourceReader, err := c.apiClient.GetResource(name)
	if err != nil {
		return resource.Resource{}, errors.Trace(err)
	}
	defer resourceReader.Close()

	if err := resDir.writeInfo(info, c.createFile); err != nil {
		return resource.Resource{}, errors.Trace(err)
	}

	content := resourceContent{
		data:        resourceReader,
		size:        info.Size,
		fingerprint: info.Fingerprint,
	}
	relPath := []string{info.Path}
	if err := resDir.writeResource(relPath, content, c.createFile); err != nil {
		return resource.Resource{}, errors.Trace(err)
	}

	return info, nil
}

// replaceResourceDir replaces the original resource dir
// with the temporary resource dir.
func (c *Context) replaceResourceDir(oldResDir *resourceDirectory) (*resourceDirectory, error) {
	oldDir := oldResDir.resolve()
	newResDir := oldResDir.move(c.dataDir)
	newDir := newResDir.resolve()

	if err := c.replaceDirectory(newDir, oldDir); err != nil {
		return nil, errors.Annotate(err, "could not replace existing resource directory")
	}

	return newResDir, nil
}

type contextDeps struct {
	tempDir          func() (string, error)
	mkdirAll         func(string) error
	removeDir        func(string) error
	replaceDirectory func(string, string) error
	createFile       func(string) (io.WriteCloser, error)
}

func newContextDeps() contextDeps {
	return contextDeps{
		tempDir:          func() (string, error) { return ioutil.TempDir("", "juju-resource-") },
		mkdirAll:         func(path string) error { return os.MkdirAll(path, 0755) },
		removeDir:        os.RemoveAll,
		replaceDirectory: replaceDirectory,
		createFile:       func(path string) (io.WriteCloser, error) { return os.Create(path) },
	}
}

// replaceDirectory replaces the target directory with the source. This
// involves removing the target if it exists and then moving the source
// into place.
func replaceDirectory(targetDir, sourceDir string) error {
	if err := os.RemoveAll(targetDir); err != nil {
		return errors.Trace(err)
	}
	if err := os.Rename(targetDir, sourceDir); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<|endoftext|>