<commit_before>package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"github.com/gonuts/logger"
)

func lbpkr_make_cmd_self_bdist_rpm() *commander.Command {
	cmd := &commander.Command{
		Run:       lbpkr_run_cmd_self_bdist_rpm,
		UsageLine: "bdist-rpm [options]",
		Short:     "create a RPM package of lbpkr",
		Long: `
bdist-rpm creates a RPM package containing lbpkr.

ex:
 $ lbpkr self bdist-rpm
 $ lbpkr self bdist-rpm -name=lbpkr
 $ lbpkr self bdist-rpm -name=lbpkr -version=0.1.20140619
 $ lbpkr self bdist-rpm -name=lbpkr -version=0.1.20140619 -release=1
`,
		Flag: *flag.NewFlagSet("lbpkr-self-bdist-rpm", flag.ExitOnError),
	}
	cmd.Flag.Bool("v", false, "enable verbose mode")
	cmd.Flag.String("type", "lhcb", "config type (lhcb|atlas)")
	cmd.Flag.String("name", "lbpkr", "name of the RPM to generate")
	cmd.Flag.String("version", Version, "version of the RPM to generate")
	cmd.Flag.Int("release", 0, "release number of the RPM to generate")
	return cmd
}

func lbpkr_run_cmd_self_bdist_rpm(cmd *commander.Command, args []string) error {
	var err error

	cfgtype := cmd.Flag.Lookup("type").Value.Get().(string)
	debug := cmd.Flag.Lookup("v").Value.Get().(bool)
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	vers := cmd.Flag.Lookup("version").Value.Get().(string)
	release := cmd.Flag.Lookup("release").Value.Get().(int)

	switch len(args) {
	case 0:
	default:
		cmd.Usage()
		return fmt.Errorf("lbpkr: invalid number of arguments. expected n=0. got=%d (%v)",
			len(args),
			args,
		)
	}

	tmpdir, err := ioutil.TempDir("", "lbpkr-self-bdist-rpm-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpdir)
	//fmt.Printf(">>> [%s]\n", tmpdir)

	rpmbuildroot := filepath.Join(tmpdir, "rpmbuild")

	cfg := NewConfig(cfgtype)
	msg := logger.New("lbpkr")
	if debug {
		msg.SetLevel(logger.DEBUG)
	}

	// if siteroot is empty, re-create the config with the according
	// Default-Siteroot value so RPM relocation will work correctly.
	if siteroot := cfg.Siteroot(); siteroot == "" {
		err = os.Setenv("MYSITEROOT", cfg.DefaultSiteroot())
		if err != nil {
			return err
		}
		cfg = NewConfig(cfgtype)
	}

	rpmbuild, err := exec.LookPath("rpmbuild")
	if err != nil {
		msg.Errorf("could not locate 'rpmbuild': %v\n", err)
		return err
	}

	tarcmd := exec.Command("lbpkr",
		"self", "bdist",
		"-type="+cfgtype,
		"-name="+name,
		"-version="+vers,
		fmt.Sprintf("-v=%v", debug),
	)
	tarcmd.Stdout = os.Stdout
	tarcmd.Stderr = os.Stderr
	tarcmd.Stdin = os.Stdin
	err = tarcmd.Run()
	if err != nil {
		msg.Errorf("could not create tarball: %v\n", err)
		return err
	}

	data := struct {
		Url       string
		Prefix    string
		BuildRoot string
		Name      string
		Version   string
		Release   int
	}{
		Url:       "http://github.com/lhcb-org/lbpkr",
		Prefix:    cfg.Siteroot(),
		BuildRoot: tmpdir,
		Name:      name,
		Version:   vers,
		Release:   release,
	}

	rpm_arch := "x86_64"
	switch runtime.GOARCH {
	case "amd64":
		rpm_arch = "x86_64"
	case "386":
		rpm_arch = "i686"
	}

	rpm_fname := fmt.Sprintf("%s-%s-%d.%s.rpm", data.Name, data.Version, data.Release, rpm_arch)
	msg.Infof("creating [%s]...\n", rpm_fname)

	// prepare a tarball with the lbpkr binary.
	dirname := fmt.Sprintf("%s-%s", data.Name, data.Version)
	fname := dirname + ".tar.gz"
	bdist_fname := dirname + "." + rpm_arch + ".tar.gz"

	// create hierarchy of dirs for rpmbuild
	for _, dir := range []string{"RPMS", "SRPMS", "BUILD", "SOURCES", "SPECS", "tmp"} {
		err = os.MkdirAll(filepath.Join(rpmbuildroot, dir), 0755)
		if err != nil {
			return err
		}
	}

	// copy tarball
	err = bincp(filepath.Join(rpmbuildroot, "SOURCES", fname), bdist_fname)
	if err != nil {
		return err
	}

	// create spec-file
	spec_fname := fmt.Sprintf("%s-%s-%d.spec", data.Name, data.Version, data.Release)
	spec, err := os.Create(filepath.Join(
		rpmbuildroot, "SPECS",
		spec_fname,
	))
	if err != nil {
		return err
	}
	defer spec.Close()
	t := template.Must(template.New("bdist-rpm-spec").Parse(rpm_tmpl))
	err = t.Execute(spec, &data)
	if err != nil {
		return err
	}
	spec.Sync()
	spec.Close()

	rpm := exec.Command(rpmbuild, "-ba", filepath.Join("SPECS", spec_fname))
	rpm.Dir = rpmbuildroot
	if debug {
		rpm.Stdout = os.Stdout
		rpm.Stderr = os.Stderr
	}

	err = rpm.Run()
	if err != nil {
		return err
	}

	err = bincp(rpm_fname, filepath.Join(rpmbuildroot, "RPMS", rpm_arch, rpm_fname))
	if err != nil {
		return err
	}

	msg.Infof("creating [%s]... [ok]\n", rpm_fname)
	return err
}

const rpm_tmpl = `
%define        __spec_install_post %{nil}
%define          debug_package %{nil}
%define        __os_install_post %{_dbpath}/brp-compress
%define _topdir   {{.BuildRoot}}/rpmbuild
%define _tmppath  %{_topdir}/tmp

Summary: lbpkr is a tool to install RPMs.
Name: {{.Name}}
Version: {{.Version}}
Release: {{.Release}}
License: BSD
Group: Science
SOURCE0 : %{name}-%{version}.tar.gz
URL: {{.Url}}

BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root

%description
%{summary}

%prep
%setup -q

%build

%install
rm -rf %{buildroot}
mkdir -p  %{buildroot}/{{.Prefix}}
/bin/cp -r ./* %{buildroot}/{{.Prefix}}

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root,-)
/{{.Prefix}}/usr/bin/lbpkr
`
<commit_msg>cmd-self-bdist-rpm: handle hysteresis effect from MYSITEROOT<commit_after>package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"github.com/gonuts/logger"
)

func lbpkr_make_cmd_self_bdist_rpm() *commander.Command {
	cmd := &commander.Command{
		Run:       lbpkr_run_cmd_self_bdist_rpm,
		UsageLine: "bdist-rpm [options]",
		Short:     "create a RPM package of lbpkr",
		Long: `
bdist-rpm creates a RPM package containing lbpkr.

ex:
 $ lbpkr self bdist-rpm
 $ lbpkr self bdist-rpm -name=lbpkr
 $ lbpkr self bdist-rpm -name=lbpkr -version=0.1.20140619
 $ lbpkr self bdist-rpm -name=lbpkr -version=0.1.20140619 -release=1
`,
		Flag: *flag.NewFlagSet("lbpkr-self-bdist-rpm", flag.ExitOnError),
	}
	cmd.Flag.Bool("v", false, "enable verbose mode")
	cmd.Flag.String("type", "lhcb", "config type (lhcb|atlas)")
	cmd.Flag.String("name", "lbpkr", "name of the RPM to generate")
	cmd.Flag.String("version", Version, "version of the RPM to generate")
	cmd.Flag.Int("release", 0, "release number of the RPM to generate")
	return cmd
}

func lbpkr_run_cmd_self_bdist_rpm(cmd *commander.Command, args []string) error {
	var err error

	cfgtype := cmd.Flag.Lookup("type").Value.Get().(string)
	debug := cmd.Flag.Lookup("v").Value.Get().(bool)
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	vers := cmd.Flag.Lookup("version").Value.Get().(string)
	release := cmd.Flag.Lookup("release").Value.Get().(int)

	switch len(args) {
	case 0:
	default:
		cmd.Usage()
		return fmt.Errorf("lbpkr: invalid number of arguments. expected n=0. got=%d (%v)",
			len(args),
			args,
		)
	}

	tmpdir, err := ioutil.TempDir("", "lbpkr-self-bdist-rpm-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpdir)
	//fmt.Printf(">>> [%s]\n", tmpdir)

	rpmbuildroot := filepath.Join(tmpdir, "rpmbuild")

	cfg := NewConfig(cfgtype)
	msg := logger.New("lbpkr")
	if debug {
		msg.SetLevel(logger.DEBUG)
	}

	// re-create the config with the according Default-Siteroot value
	// so RPM relocation will work correctly.
	err = os.Setenv("MYSITEROOT", cfg.DefaultSiteroot())
	if err != nil {
		return err
	}
	cfg = NewConfig(cfgtype)

	rpmbuild, err := exec.LookPath("rpmbuild")
	if err != nil {
		msg.Errorf("could not locate 'rpmbuild': %v\n", err)
		return err
	}

	tarcmd := exec.Command("lbpkr",
		"self", "bdist",
		"-type="+cfgtype,
		"-name="+name,
		"-version="+vers,
		fmt.Sprintf("-v=%v", debug),
	)
	tarcmd.Stdout = os.Stdout
	tarcmd.Stderr = os.Stderr
	tarcmd.Stdin = os.Stdin
	err = tarcmd.Run()
	if err != nil {
		msg.Errorf("could not create tarball: %v\n", err)
		return err
	}

	data := struct {
		Url       string
		Prefix    string
		BuildRoot string
		Name      string
		Version   string
		Release   int
	}{
		Url:       "http://github.com/lhcb-org/lbpkr",
		Prefix:    cfg.Siteroot(),
		BuildRoot: tmpdir,
		Name:      name,
		Version:   vers,
		Release:   release,
	}

	rpm_arch := "x86_64"
	switch runtime.GOARCH {
	case "amd64":
		rpm_arch = "x86_64"
	case "386":
		rpm_arch = "i686"
	}

	rpm_fname := fmt.Sprintf("%s-%s-%d.%s.rpm", data.Name, data.Version, data.Release, rpm_arch)
	msg.Infof("creating [%s]...\n", rpm_fname)

	// prepare a tarball with the lbpkr binary.
	dirname := fmt.Sprintf("%s-%s", data.Name, data.Version)
	fname := dirname + ".tar.gz"
	bdist_fname := dirname + "." + rpm_arch + ".tar.gz"

	// create hierarchy of dirs for rpmbuild
	for _, dir := range []string{"RPMS", "SRPMS", "BUILD", "SOURCES", "SPECS", "tmp"} {
		err = os.MkdirAll(filepath.Join(rpmbuildroot, dir), 0755)
		if err != nil {
			return err
		}
	}

	// copy tarball
	err = bincp(filepath.Join(rpmbuildroot, "SOURCES", fname), bdist_fname)
	if err != nil {
		return err
	}

	// create spec-file
	spec_fname := fmt.Sprintf("%s-%s-%d.spec", data.Name, data.Version, data.Release)
	spec, err := os.Create(filepath.Join(
		rpmbuildroot, "SPECS",
		spec_fname,
	))
	if err != nil {
		return err
	}
	defer spec.Close()
	t := template.Must(template.New("bdist-rpm-spec").Parse(rpm_tmpl))
	err = t.Execute(spec, &data)
	if err != nil {
		return err
	}
	spec.Sync()
	spec.Close()

	rpm := exec.Command(rpmbuild, "-ba", filepath.Join("SPECS", spec_fname))
	rpm.Dir = rpmbuildroot
	if debug {
		rpm.Stdout = os.Stdout
		rpm.Stderr = os.Stderr
	}

	err = rpm.Run()
	if err != nil {
		return err
	}

	err = bincp(rpm_fname, filepath.Join(rpmbuildroot, "RPMS", rpm_arch, rpm_fname))
	if err != nil {
		return err
	}

	msg.Infof("creating [%s]... [ok]\n", rpm_fname)
	return err
}

const rpm_tmpl = `
%define        __spec_install_post %{nil}
%define          debug_package %{nil}
%define        __os_install_post %{_dbpath}/brp-compress
%define _topdir   {{.BuildRoot}}/rpmbuild
%define _tmppath  %{_topdir}/tmp

Summary: lbpkr is a tool to install RPMs.
Name: {{.Name}}
Version: {{.Version}}
Release: {{.Release}}
License: BSD
Group: Science
SOURCE0 : %{name}-%{version}.tar.gz
URL: {{.Url}}

BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root

%description
%{summary}

%prep
%setup -q

%build

%install
rm -rf %{buildroot}
mkdir -p  %{buildroot}/{{.Prefix}}
/bin/cp -r ./* %{buildroot}/{{.Prefix}}

%clean
rm -rf %{buildroot}

%files
%defattr(-,root,root,-)
/{{.Prefix}}/usr/bin/lbpkr
`
<|endoftext|>


<commit_before>package collaboration

import (
	"os"
	"path/filepath"

	"github.com/boltdb/bolt"
)

const (
	DatabasePath = "/opt/kite/klient/klient.db"
	UserBucket   = "users"
)

func NewBoltStorage() (*boltdb, error) {
	d := &boltdb{}

	// Ensure data directory exists.
	dir := filepath.Dir(DatabasePath)
	if err := os.MkdirAll(dir, 0700); err != nil {
		return nil, err
	}

	if err := d.open(DatabasePath); err != nil {
		return nil, err
	}

	return d, nil
}

// boltdb satisfies Storage interface
type boltdb struct {
	*bolt.DB
}

func (b *boltdb) open(dbpath string) error {
	var err error
	b.DB, err = bolt.Open(dbpath, 0600, nil)
	if err != nil {
		return err
	}

	return b.Update(func(tx *Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(UserBucket))
		if err != nil {
			return err
		}
		return nil
	})
}

func (b *boltdb) Get(username string) (string, error) {
	var user string

	err := b.View(func(tx *Tx) error {
		value := tx.User(username)
		if len(value) == 0 {
			return ErrUserNotFound
		}
		user = value
		return nil
	})

	if err != nil {
		return "", err
	}

	return user, nil
}

// GetAll fetches all keys which are unique usernames in the bucket.
func (b *boltdb) GetAll() ([]string, error) {
	users := make([]string, 0)

	err := b.View(func(tx *Tx) error {
		tx.Bucket([]byte(UserBucket)).ForEach(func(k, _ []byte) error {
			users = append(users, string(k))
			return nil
		})
		return nil
	})

	if err != nil {
		return nil, err
	}
	return users, nil
}

func (b *boltdb) Set(username, value string) error {
	return b.Update(func(tx *Tx) error {
		return tx.SetUser(username, value)
	})
}

func (b *boltdb) Delete(username string) error {
	return b.Update(func(tx *Tx) error {
		return tx.DeleteUser(username)
	})
}

func (b *boltdb) Close() error {
	return b.DB.Close()
}

// Tx is our own transaction type which provides helper methods
type Tx struct {
	*bolt.Tx
}

// User retrieves a users field by name.
func (tx *Tx) User(key string) string {
	return string(tx.Bucket([]byte(UserBucket)).Get([]byte(key)))
}

// SetUser sets the value of a users field by name.
func (tx *Tx) SetUser(key, value string) error {
	return tx.Bucket([]byte(UserBucket)).Put([]byte(key), []byte(value))
}

// DeleteUser deletes the key of a users field by name.
func (tx *Tx) DeleteUser(key string) error {
	return tx.Bucket([]byte(UserBucket)).Delete([]byte(key))
}

// View executes a function in the context of a read-only transaction.
func (b *boltdb) View(fn func(*Tx) error) error {
	return b.DB.View(func(tx *bolt.Tx) error {
		return fn(&Tx{tx})
	})
}

// Update executes a function in the context of a writable transaction.
func (b *boltdb) Update(fn func(*Tx) error) error {
	return b.DB.Update(func(tx *bolt.Tx) error {
		return fn(&Tx{tx})
	})
}
<commit_msg>klient: Use $HOME/.config/koding/klient.bolt for the storage path.<commit_after>package collaboration

import (
	"os"
	"os/user"
	"path/filepath"
	"time"

	"github.com/boltdb/bolt"
)

const (
	UserBucket = "users"
	// Canonical database path is `$HOME + DatabasePath`
	DatabasePath = "/.config/koding/klient.bolt"
)

// NewBoltStorage returns a new boltdb
func NewBoltStorage() (*boltdb, error) {
	d := &boltdb{}

	// Ensure data directory exists.
	u, err := user.Current()
	if err != nil {
		return nil, err
	}
	boltPath := u.HomeDir + DatabasePath
	if err := os.MkdirAll(filepath.Dir(boltPath), 0755); err != nil {
		return nil, err
	}

	if err := d.open(boltPath); err != nil {
		return nil, err
	}

	return d, nil
}

// boltdb satisfies Storage interface
type boltdb struct {
	*bolt.DB
}

func (b *boltdb) open(dbpath string) error {
	var err error
	b.DB, err = bolt.Open(dbpath, 0644, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}

	return b.Update(func(tx *Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(UserBucket))
		if err != nil {
			return err
		}
		return nil
	})
}

// Get returns the value of a given username
func (b *boltdb) Get(username string) (string, error) {
	var user string

	err := b.View(func(tx *Tx) error {
		value := tx.User(username)
		if len(value) == 0 {
			return ErrUserNotFound
		}
		user = value
		return nil
	})

	if err != nil {
		return "", err
	}

	return user, nil
}

// GetAll fetches all keys which are unique usernames in the bucket.
func (b *boltdb) GetAll() ([]string, error) {
	users := make([]string, 0)

	err := b.View(func(tx *Tx) error {
		tx.Bucket([]byte(UserBucket)).ForEach(func(k, _ []byte) error {
			users = append(users, string(k))
			return nil
		})
		return nil
	})

	if err != nil {
		return nil, err
	}
	return users, nil
}

// Set assigns the value for the given username
func (b *boltdb) Set(username, value string) error {
	return b.Update(func(tx *Tx) error {
		return tx.SetUser(username, value)
	})
}

// Delete deletes the given username key and value from the bucket
func (b *boltdb) Delete(username string) error {
	return b.Update(func(tx *Tx) error {
		return tx.DeleteUser(username)
	})
}

// Close closes the boltdb database for further read and writes
func (b *boltdb) Close() error {
	return b.DB.Close()
}

// View executes a function in the context of a read-only transaction.
func (b *boltdb) View(fn func(*Tx) error) error {
	return b.DB.View(func(tx *bolt.Tx) error {
		return fn(&Tx{tx})
	})
}

// Update executes a function in the context of a writable transaction.
func (b *boltdb) Update(fn func(*Tx) error) error {
	return b.DB.Update(func(tx *bolt.Tx) error {
		return fn(&Tx{tx})
	})
}

// Tx is our own transaction type which provides helper methods
type Tx struct {
	*bolt.Tx
}

// User retrieves a users field by name.
func (tx *Tx) User(key string) string {
	return string(tx.Bucket([]byte(UserBucket)).Get([]byte(key)))
}

// SetUser sets the value of a users field by name.
func (tx *Tx) SetUser(key, value string) error {
	return tx.Bucket([]byte(UserBucket)).Put([]byte(key), []byte(value))
}

// DeleteUser deletes the key of a users field by name.
func (tx *Tx) DeleteUser(key string) error {
	return tx.Bucket([]byte(UserBucket)).Delete([]byte(key))
}
<|endoftext|>


<commit_before>package main

import (
	"regexp"
	"testing"
)

func TestColumnAddressValue(t *testing.T) {
	var tests = []struct {
		inColumn TypedColumn
		outValue string
	}{
		{
			inColumn: ColumnAddressContinent{},
			outValue: "^[a-zA-Z ]+$",
		},
		{
			inColumn: ColumnAddressCountry{},
			outValue: "^[a-zA-Z ,]+$",
		},
		{
			inColumn: ColumnAddressCity{},
			outValue: "^[- a-zA-Z.]+$",
		},
		{
			inColumn: ColumnAddressPhone{},
			outValue: "^[0-9-]+$",
		},
		{
			inColumn: ColumnAddressState{},
			outValue: "^[- a-zA-Z.]+$",
		},
		{
			inColumn: ColumnAddressState{},
			outValue: "^[- a-zA-Z.]+$",
		},
		{
			inColumn: ColumnAddressStreet{},
			outValue: "^[- a-zA-Z.0-9]+$",
		},
		{
			inColumn: ColumnAddressZip{},
			outValue: "^[0-9]+$",
		},
	}

	for _, test := range tests {
		actual, _ := test.inColumn.Value(nil)
		matched, _ := regexp.MatchString(test.outValue, actual)
		if !matched {
			t.Fatalf("Expected: %v\nActual: %v", test.outValue, actual)
		}
	}
}
<commit_msg>test: fix test regex for column: `address.state`<commit_after>package main

import (
	"regexp"
	"testing"
)

func TestColumnAddressValue(t *testing.T) {
	var tests = []struct {
		inColumn TypedColumn
		outValue string
	}{
		{
			inColumn: ColumnAddressContinent{},
			outValue: "^[a-zA-Z ]+$",
		},
		{
			inColumn: ColumnAddressCountry{},
			outValue: "^[a-zA-Z ,]+$",
		},
		{
			inColumn: ColumnAddressCity{},
			outValue: "^[- a-zA-Z.]+$",
		},
		{
			inColumn: ColumnAddressPhone{},
			outValue: "^[0-9-]+$",
		},
		{
			inColumn: ColumnAddressState{},
			outValue: "^[- a-zA-Z.()]+$",
		},
		{
			inColumn: ColumnAddressStreet{},
			outValue: "^[- a-zA-Z.0-9]+$",
		},
		{
			inColumn: ColumnAddressZip{},
			outValue: "^[0-9]+$",
		},
	}

	for _, test := range tests {
		actual, _ := test.inColumn.Value(nil)
		matched, _ := regexp.MatchString(test.outValue, actual)
		if !matched {
			t.Fatalf("Expected: %v\nActual: %v", test.outValue, actual)
		}
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"strings"

	cmds "github.com/jbenet/go-ipfs/commands"
	u "github.com/jbenet/go-ipfs/util"
)

// ErrInvalidSubcmd signals when the parse error is not found
var ErrInvalidSubcmd = errors.New("subcommand not found")

// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) {
	path, input, cmd := parsePath(input, root)
	if len(path) == 0 {
		return nil, nil, path, ErrInvalidSubcmd
	}

	opts, stringVals, err := parseOptions(input)
	if err != nil {
		return nil, cmd, path, err
	}

	optDefs, err := root.GetOptions(path)
	if err != nil {
		return nil, cmd, path, err
	}

	// check to make sure there aren't any undefined options
	for k := range opts {
		if _, found := optDefs[k]; !found {
			err = fmt.Errorf("Unrecognized option: -%s", k)
			return nil, cmd, path, err
		}
	}

	req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs)
	if err != nil {
		return nil, cmd, path, err
	}

	recursive, _, err := req.Option(cmds.RecShort).Bool()
	if err != nil {
		return nil, nil, nil, u.ErrCast()
	}
	stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive)
	if err != nil {
		return nil, cmd, path, err
	}
	req.SetArguments(stringArgs)

	file := &cmds.SliceFile{"", fileArgs}
	req.SetFiles(file)

	err = cmd.CheckArguments(req)
	if err != nil {
		return req, cmd, path, err
	}

	return req, cmd, path, nil
}

// parsePath separates the command path and the opts and args from a command string
// returns command path slice, rest slice, and the corresponding *cmd.Command
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
	cmd := root
	i := 0

	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}

		sub := cmd.Subcommand(blob)
		if sub == nil {
			break
		}
		cmd = sub

		i++
	}

	return input[:i], input[i:], cmd
}

// parseOptions parses the raw string values of the given options
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
	opts := make(map[string]interface{})
	args := []string{}

	for i := 0; i < len(input); i++ {
		blob := input[i]

		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""

			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}

			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}

			if _, ok := opts[name]; ok {
				return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
			}

			opts[name] = value

		} else {
			args = append(args, blob)
		}
	}

	return opts, args, nil
}

func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []cmds.File, error) {
	// check if stdin is coming from terminal or is being piped in
	if stdin != nil {
		stat, err := stdin.Stat()
		if err != nil {
			return nil, nil, err
		}

		// if stdin isn't a CharDevice, set it to nil
		// (this means it is coming from terminal and we want to ignore it)
		if (stat.Mode() & os.ModeCharDevice) != 0 {
			stdin = nil
		}
	}

	// count required argument definitions
	numRequired := 0
	for _, argDef := range argDefs {
		if argDef.Required {
			numRequired++
		}
	}

	// count number of values provided by user
	numInputs := len(inputs)
	if stdin != nil {
		numInputs += 1
	}

	// if we have more arg values provided than argument definitions,
	// and the last arg definition is not variadic (or there are no definitions), return an error
	notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic
	if notVariadic && numInputs > len(argDefs) {
		return nil, nil, fmt.Errorf("Expected %v arguments, got %v", len(argDefs), numInputs)
	}

	stringArgs := make([]string, 0, numInputs)
	fileArgs := make([]cmds.File, 0, numInputs)

	argDefIndex := 0 // the index of the current argument definition
	for i, input := range inputs {
		// get the argument definiton (should be argDefs[argDefIndex],
		// but if argDefIndex > len(argDefs) we use the last argument definition)
		var argDef cmds.Argument
		if argDefIndex < len(argDefs) {
			argDef = argDefs[argDefIndex]
		} else if len(argDefs) > 0 {
			argDef = argDefs[len(argDefs)-1]
		}

		// skip optional argument definitions if there aren't sufficient remaining inputs
		if numInputs-i <= numRequired && !argDef.Required {
			continue
		} else if argDef.Required {
			numRequired--
		}

		if argDef.Type == cmds.ArgString {
			if stdin == nil {
				// add string values
				stringArgs = append(stringArgs, input)
				inputs = inputs[1:]

			} else if argDef.SupportsStdin {
				// if we have a stdin, read it in and use the data as a string value
				var buf bytes.Buffer
				_, err := buf.ReadFrom(stdin)
				if err != nil {
					return nil, nil, err
				}
				stringArgs = append(stringArgs, buf.String())
				stdin = nil
			}

		} else if argDef.Type == cmds.ArgFile {
			if stdin == nil {
				// treat stringArg values as file paths
				path := input
				inputs = inputs[1:]

				file, err := os.Open(path)
				if err != nil {
					return nil, nil, err
				}

				stat, err := file.Stat()
				if err != nil {
					return nil, nil, err
				}

				if stat.IsDir() {
					if !argDef.Recursive {
						err = fmt.Errorf("Invalid path '%s', argument '%s' does not support directories",
							input, argDef.Name)
						return nil, nil, err
					}
					if !recursive {
						err = fmt.Errorf("'%s' is a directory, use the '-%s' flag to specify directories",
							input, cmds.RecShort)
						return nil, nil, err
					}
				}

				fileArg, err := getFile(file, input)
				if err != nil {
					return nil, nil, err
				}

				fileArgs = append(fileArgs, fileArg)

			} else if argDef.SupportsStdin {
				// if we have a stdin, create a file from it
				fileArg := &cmds.ReaderFile{"", stdin}
				fileArgs = append(fileArgs, fileArg)
				stdin = nil
			}
		}

		argDefIndex++
	}

	// check to make sure we didn't miss any required arguments
	if len(argDefs) > argDefIndex {
		for _, argDef := range argDefs[argDefIndex:] {
			if argDef.Required {
				return nil, nil, fmt.Errorf("Argument '%s' is required", argDef.Name)
			}
		}
	}

	return stringArgs, fileArgs, nil
}

// recursively get file or directory contents as a cmds.File
func getFile(file *os.File, path string) (cmds.File, error) {
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}

	// for non-directories, return a ReaderFile
	if !stat.IsDir() {
		return &cmds.ReaderFile{path, file}, nil
	}

	// for directories, recursively iterate though children then return as a SliceFile
	contents, err := file.Readdir(0)
	if err != nil {
		return nil, err
	}

	files := make([]cmds.File, 0, len(contents))

	for _, child := range contents {
		childPath := fmt.Sprintf("%s/%s", path, child.Name())
		childFile, err := os.Open(childPath)
		if err != nil {
			return nil, err
		}

		f, err := getFile(childFile, childPath)
		if err != nil {
			return nil, err
		}

		files = append(files, f)
	}

	return &cmds.SliceFile{path, files}, nil
}
<commit_msg>commands/cli: Fixed stdin arg handling<commit_after>package cli

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"strings"

	cmds "github.com/jbenet/go-ipfs/commands"
	u "github.com/jbenet/go-ipfs/util"
)

// ErrInvalidSubcmd signals when the parse error is not found
var ErrInvalidSubcmd = errors.New("subcommand not found")

// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) {
	path, input, cmd := parsePath(input, root)
	if len(path) == 0 {
		return nil, nil, path, ErrInvalidSubcmd
	}

	opts, stringVals, err := parseOptions(input)
	if err != nil {
		return nil, cmd, path, err
	}

	optDefs, err := root.GetOptions(path)
	if err != nil {
		return nil, cmd, path, err
	}

	// check to make sure there aren't any undefined options
	for k := range opts {
		if _, found := optDefs[k]; !found {
			err = fmt.Errorf("Unrecognized option: -%s", k)
			return nil, cmd, path, err
		}
	}

	req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs)
	if err != nil {
		return nil, cmd, path, err
	}

	recursive, _, err := req.Option(cmds.RecShort).Bool()
	if err != nil {
		return nil, nil, nil, u.ErrCast()
	}
	stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive)
	if err != nil {
		return nil, cmd, path, err
	}
	req.SetArguments(stringArgs)

	file := &cmds.SliceFile{"", fileArgs}
	req.SetFiles(file)

	err = cmd.CheckArguments(req)
	if err != nil {
		return req, cmd, path, err
	}

	return req, cmd, path, nil
}

// parsePath separates the command path and the opts and args from a command string
// returns command path slice, rest slice, and the corresponding *cmd.Command
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
	cmd := root
	i := 0

	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}

		sub := cmd.Subcommand(blob)
		if sub == nil {
			break
		}
		cmd = sub

		i++
	}

	return input[:i], input[i:], cmd
}

// parseOptions parses the raw string values of the given options
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
	opts := make(map[string]interface{})
	args := []string{}

	for i := 0; i < len(input); i++ {
		blob := input[i]

		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""

			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}

			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}

			if _, ok := opts[name]; ok {
				return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
			}

			opts[name] = value

		} else {
			args = append(args, blob)
		}
	}

	return opts, args, nil
}

func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []cmds.File, error) {
	// check if stdin is coming from terminal or is being piped in
	if stdin != nil {
		stat, err := stdin.Stat()
		if err != nil {
			return nil, nil, err
		}

		// if stdin isn't a CharDevice, set it to nil
		// (this means it is coming from terminal and we want to ignore it)
		if (stat.Mode() & os.ModeCharDevice) != 0 {
			stdin = nil
		}
	}

	// count required argument definitions
	numRequired := 0
	for _, argDef := range argDefs {
		if argDef.Required {
			numRequired++
		}
	}

	// count number of values provided by user
	numInputs := len(inputs)
	if stdin != nil {
		numInputs += 1
	}

	// if we have more arg values provided than argument definitions,
	// and the last arg definition is not variadic (or there are no definitions), return an error
	notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic
	if notVariadic && numInputs > len(argDefs) {
		return nil, nil, fmt.Errorf("Expected %v arguments, got %v", len(argDefs), numInputs)
	}

	stringArgs := make([]string, 0, numInputs)
	fileArgs := make([]cmds.File, 0, numInputs)

	argDefIndex := 0 // the index of the current argument definition
	for i := 0; i < numInputs; i++ {
		// get the argument definiton (should be argDefs[argDefIndex],
		// but if argDefIndex > len(argDefs) we use the last argument definition)
		var argDef cmds.Argument
		if argDefIndex < len(argDefs) {
			argDef = argDefs[argDefIndex]
		} else if len(argDefs) > 0 {
			argDef = argDefs[len(argDefs)-1]
		}

		// skip optional argument definitions if there aren't sufficient remaining inputs
		if numInputs-i <= numRequired && !argDef.Required {
			continue
		} else if argDef.Required {
			numRequired--
		}

		if argDef.Type == cmds.ArgString {
			if stdin == nil {
				// add string values
				stringArgs = append(stringArgs, inputs[0])
				inputs = inputs[1:]

			} else if argDef.SupportsStdin {
				// if we have a stdin, read it in and use the data as a string value
				var buf bytes.Buffer
				_, err := buf.ReadFrom(stdin)
				if err != nil {
					return nil, nil, err
				}
				stringArgs = append(stringArgs, buf.String())
				stdin = nil
			}

		} else if argDef.Type == cmds.ArgFile {
			if stdin == nil {
				// treat stringArg values as file paths
				path := inputs[0]
				inputs = inputs[1:]

				file, err := os.Open(path)
				if err != nil {
					return nil, nil, err
				}

				stat, err := file.Stat()
				if err != nil {
					return nil, nil, err
				}

				if stat.IsDir() {
					if !argDef.Recursive {
						err = fmt.Errorf("Invalid path '%s', argument '%s' does not support directories",
							path, argDef.Name)
						return nil, nil, err
					}
					if !recursive {
						err = fmt.Errorf("'%s' is a directory, use the '-%s' flag to specify directories",
							path, cmds.RecShort)
						return nil, nil, err
					}
				}

				fileArg, err := getFile(file, path)
				if err != nil {
					return nil, nil, err
				}

				fileArgs = append(fileArgs, fileArg)

			} else if argDef.SupportsStdin {
				// if we have a stdin, create a file from it
				fileArg := &cmds.ReaderFile{"", stdin}
				fileArgs = append(fileArgs, fileArg)
				stdin = nil
			}
		}

		argDefIndex++
	}

	// check to make sure we didn't miss any required arguments
	if len(argDefs) > argDefIndex {
		for _, argDef := range argDefs[argDefIndex:] {
			if argDef.Required {
				return nil, nil, fmt.Errorf("Argument '%s' is required", argDef.Name)
			}
		}
	}

	return stringArgs, fileArgs, nil
}

// recursively get file or directory contents as a cmds.File
func getFile(file *os.File, path string) (cmds.File, error) {
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}

	// for non-directories, return a ReaderFile
	if !stat.IsDir() {
		return &cmds.ReaderFile{path, file}, nil
	}

	// for directories, recursively iterate though children then return as a SliceFile
	contents, err := file.Readdir(0)
	if err != nil {
		return nil, err
	}

	files := make([]cmds.File, 0, len(contents))

	for _, child := range contents {
		childPath := fmt.Sprintf("%s/%s", path, child.Name())
		childFile, err := os.Open(childPath)
		if err != nil {
			return nil, err
		}

		f, err := getFile(childFile, childPath)
		if err != nil {
			return nil, err
		}

		files = append(files, f)
	}

	return &cmds.SliceFile{path, files}, nil
}
<|endoftext|>


<commit_before>package common

import (
	"net"
	"os"
	"testing"
	"time"
)

const (
	confFile = "../bin/quantum-test.yml"
)

var (
	testPacket []byte
)

func init() {
	testPacket = make([]byte, 18)
	// IP (1.1.1.1)
	testPacket[0] = 1
	testPacket[1] = 1
	testPacket[2] = 1
	testPacket[3] = 1

	// Nonce
	testPacket[4] = 2
	testPacket[5] = 2
	testPacket[6] = 2
	testPacket[7] = 2
	testPacket[8] = 2
	testPacket[9] = 2
	testPacket[10] = 2
	testPacket[11] = 2
	testPacket[12] = 2
	testPacket[13] = 2
	testPacket[14] = 2
	testPacket[15] = 2

	// Packet data
	testPacket[16] = 3
	testPacket[17] = 3
}

func testEq(a, b []byte) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

func TestArrayEquals(t *testing.T) {
	if !ArrayEquals(nil, nil) {
		t.Fatal("ArrayEquals returned false comparing nil/nil")
	}
	if ArrayEquals([]byte{0}, nil) {
		t.Fatal("ArrayEquals returned true comparing nil/non-nil")
	}
	if ArrayEquals([]byte{0, 1}, []byte{0}) {
		t.Fatal("ArrayEquals returned true comparing mismatched lengths")
	}
	if !ArrayEquals([]byte{0, 1}, []byte{0, 1}) {
		t.Fatal("ArrayEquals returned false for equal arrays")
	}
}

func TestIPtoInt(t *testing.T) {
	var expected uint32
	actual := IPtoInt(net.ParseIP("0.0.0.0"))
	if expected != actual {
		t.Fatalf("IPtoInt did not return the right value, got: %d, expected: %d", actual, expected)
	}
}

func TestIncrementIP(t *testing.T) {
	expected := net.ParseIP("10.0.0.1")

	actual := net.ParseIP("10.0.0.0")
	IncrementIP(actual)

	if !testEq(expected, actual) {
		t.Fatalf("IncrementIP did not return the right value, got: %s, expected: %s", actual, expected)
	}
}

func TestNewConfig(t *testing.T) {
	os.Setenv("QUANTUM_DEVICE_NAME", "different")
	os.Setenv("QUANTUM_LISTEN_PORT", "1")
	os.Setenv("QUANTUM_CONF_FILE", confFile)
	os.Setenv("QUANTUM_PID_FILE", "../quantum.pid")

	os.Args = append(os.Args, "-n", "1", "--prefix", "woot", "--tls-skip-verify")
	cfg, err := NewConfig()
	if err != nil {
		t.Fatalf("NewConfig returned an error, %s", err)
	}
	t.Log(cfg)
	if cfg == nil {
		t.Fatal("NewConfig returned a blank config")
	}
	if cfg.DeviceName != "different" {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for DeviceName")
	}
	if cfg.ListenPort != 1 {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for ListenPort")
	}
	if cfg.Password != "Password1" {
		t.Fatalf("NewConfig didn't pick up the config file replacement for Password")
	}
	if cfg.Prefix != "woot" {
		t.Fatal("NewConfig didn't pick up the cli replacement for Prefix")
	}
	if cfg.NumWorkers != 1 {
		t.Fatal("NewConfig didn't pick up the cli replacement for NumWorkers")
	}
	if !cfg.TLSSkipVerify {
		t.Fatal("NewConfig didn't pick up the cli replacement for TLSSkipVerify")
	}

	cfg.usage(false)
	cfg.version(false)
}

func TestEcdh(t *testing.T) {
	pub, priv := GenerateECKeyPair()
	if len(pub) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the public key,\nactual: %d, expected: %d", len(pub), keyLength)
	}
	if len(priv) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the private key,\nactual: %d, expected: %d", len(priv), keyLength)
	}
	if testEq(pub, priv) {
		t.Fatalf("GenerateECKeyPair returned identical pub/priv keys this can't possibly happen:\npub: %v, priv: %v", pub, priv)
	}
	secret := GenerateSharedSecret(pub, priv)
	if len(secret) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the shared secret,\nactual: %d, expected: %d", len(secret), keyLength)
	}
	if testEq(secret, pub) || testEq(secret, priv) {
		t.Fatalf("GenerateECKeyPair returned identical secret and pub/priv keys this can't possibly happen:\npub: %v, priv: %v, secret: %v", pub, priv, secret)
	}
}

func TestNewMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	actual := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	if !testEq(actual.IPv4, publicip) || !testEq(actual.IPv6, publicip6) || actual.Port != publicport || !testEq(actual.PrivateIP, privateIP) || !testEq(actual.PublicKey, publicKey) {
		t.Fatalf("NewMapping did not return the right value, got: %v", actual)
	}
}

func TestParseMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	expected := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	actual, err := ParseMapping(expected.String(), make([]byte, 32))
	if err != nil {
		t.Fatalf("Error occured during test: %s", err)
	}
	if !testEq(actual.IPv4, expected.IPv4) || actual.Port != expected.Port || !testEq(actual.PrivateIP, expected.PrivateIP) || !testEq(actual.PublicKey, expected.PublicKey) {
		t.Fatalf("ParseMapping did not return the right value, got: %v, expected: %v", actual, expected)
	}
}

func TestParseNetworkConfig(t *testing.T) {
	actual, err := ParseNetworkConfig(DefaultNetworkConfig.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != DefaultNetworkConfig.Network || actual.LeaseTime != DefaultNetworkConfig.LeaseTime {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, DefaultNetworkConfig)
	}
}

func TestParseNetworkConfigOnlyNetwork(t *testing.T) {
	netCfg := &NetworkConfig{Network: "10.10.0.0/16"}
	actual, err := ParseNetworkConfig(netCfg.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != netCfg.Network || actual.LeaseTime != 48*time.Hour {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, netCfg)
	}
}

func TestNewTunPayload(t *testing.T) {
	payload := NewTunPayload(testPacket, 2)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewSockPayload(t *testing.T) {
	payload := NewSockPayload(testPacket, 18)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewStats(t *testing.T) {
	stats := NewStats(1)
	if stats.Packets != 0 {
		t.Fatalf("NewStats did not return the correct default for Packets, got: %d, expected: %d", stats.Packets, 0)
	}
	if stats.Bytes != 0 {
		t.Fatalf("NewStats did not return the correct default for Bytes, got: %d, expected: %d", stats.Bytes, 0)
	}
	if stats.Links == nil {
		t.Fatalf("NewStats did not return the correct default for Links, got: %v, expected: %v", stats.Links, make(map[string]*Stats))
	}
	str := stats.String()
	if str == "" {
		t.Fatalf("String didn't return the correct value.")
	}
}

func TestNewLogger(t *testing.T) {
	log := NewLogger()
	if log.Error == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
	if log.Info == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
}

func TestGenerateLocalMapping(t *testing.T) {
	cfg := &Config{
		PrivateIP:     net.ParseIP("10.10.0.1"),
		PublicIPv4:    net.ParseIP("192.167.0.1"),
		PublicIPv6:    net.ParseIP("fd00:dead:beef::2"),
		ListenPort:    1099,
		PublicKey:     make([]byte, 32),
		NetworkConfig: DefaultNetworkConfig,
		MachineID:     "123",
	}

	mappings := make(map[uint32]*Mapping)
	mapping, err := GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	if !testEq(mapping.PrivateIP.To4(), cfg.PrivateIP.To4()) {
		t.Fatal("GenerateLocalMapping created the wrong mapping.")
	}

	mappings[IPtoInt(cfg.PrivateIP)] = mapping

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	mapping.MachineID = "456"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err == nil {
		t.Fatal("GenerateLocalMapping failed to properly handle an existing ip address")
	}

	cfg.PrivateIP = nil
	mapping.MachineID = "123"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	cfg.PrivateIP = nil
	_, err = GenerateLocalMapping(cfg, make(map[uint32]*Mapping))
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Acctually one more quick update<commit_after>package common

import (
	"net"
	"os"
	"testing"
	"time"
)

const (
	confFile = "../bin/quantum-test.yml"
)

var (
	testPacket []byte
)

func init() {
	testPacket = make([]byte, 18)
	// IP (1.1.1.1)
	testPacket[0] = 1
	testPacket[1] = 1
	testPacket[2] = 1
	testPacket[3] = 1

	// Nonce
	testPacket[4] = 2
	testPacket[5] = 2
	testPacket[6] = 2
	testPacket[7] = 2
	testPacket[8] = 2
	testPacket[9] = 2
	testPacket[10] = 2
	testPacket[11] = 2
	testPacket[12] = 2
	testPacket[13] = 2
	testPacket[14] = 2
	testPacket[15] = 2

	// Packet data
	testPacket[16] = 3
	testPacket[17] = 3
}

func testEq(a, b []byte) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

func TestArrayEquals(t *testing.T) {
	if !ArrayEquals(nil, nil) {
		t.Fatal("ArrayEquals returned false comparing nil/nil")
	}
	if ArrayEquals([]byte{0}, nil) {
		t.Fatal("ArrayEquals returned true comparing nil/non-nil")
	}
	if ArrayEquals([]byte{0, 1}, []byte{0}) {
		t.Fatal("ArrayEquals returned true comparing mismatched lengths")
	}
	if !ArrayEquals([]byte{0, 1}, []byte{0, 1}) {
		t.Fatal("ArrayEquals returned false for equal arrays")
	}
}

func TestIPtoInt(t *testing.T) {
	var expected uint32
	actual := IPtoInt(net.ParseIP("0.0.0.0"))
	if expected != actual {
		t.Fatalf("IPtoInt did not return the right value, got: %d, expected: %d", actual, expected)
	}
}

func TestIncrementIP(t *testing.T) {
	expected := net.ParseIP("10.0.0.1")

	actual := net.ParseIP("10.0.0.0")
	IncrementIP(actual)

	if !testEq(expected, actual) {
		t.Fatalf("IncrementIP did not return the right value, got: %s, expected: %s", actual, expected)
	}
}

func TestNewConfig(t *testing.T) {
	os.Setenv("QUANTUM_DEVICE_NAME", "different")
	os.Setenv("QUANTUM_LISTEN_PORT", "1")
	os.Setenv("QUANTUM_CONF_FILE", confFile)
	os.Setenv("QUANTUM_PID_FILE", "../quantum.pid")

	os.Args = append(os.Args, "-n", "1", "--prefix", "woot", "--tls-skip-verify")
	cfg, err := NewConfig()
	if err != nil {
		t.Fatalf("NewConfig returned an error, %s", err)
	}
	t.Log(cfg)
	if cfg == nil {
		t.Fatal("NewConfig returned a blank config")
	}
	if cfg.DeviceName != "different" {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for DeviceName")
	}
	if cfg.ListenPort != 1 {
		t.Fatalf("NewConfig didn't pick up the environment variable replacement for ListenPort")
	}
	if cfg.Password != "Password1" {
		t.Fatalf("NewConfig didn't pick up the config file replacement for Password")
	}
	if cfg.Prefix != "woot" {
		t.Fatal("NewConfig didn't pick up the cli replacement for Prefix")
	}
	if cfg.NumWorkers != 1 {
		t.Fatal("NewConfig didn't pick up the cli replacement for NumWorkers")
	}
	if !cfg.TLSSkipVerify {
		t.Fatal("NewConfig didn't pick up the cli replacement for TLSSkipVerify")
	}

	cfg.usage(false)
	cfg.version(false)
}

func TestEcdh(t *testing.T) {
	pub, priv := GenerateECKeyPair()
	if len(pub) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the public key,\nactual: %d, expected: %d", len(pub), keyLength)
	}
	if len(priv) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the private key,\nactual: %d, expected: %d", len(priv), keyLength)
	}
	if testEq(pub, priv) {
		t.Fatalf("GenerateECKeyPair returned identical pub/priv keys this can't possibly happen:\npub: %v, priv: %v", pub, priv)
	}
	secret := GenerateSharedSecret(pub, priv)
	if len(secret) != keyLength {
		t.Fatalf("GenerateECKeyPair did not return the right length for the shared secret,\nactual: %d, expected: %d", len(secret), keyLength)
	}
	if testEq(secret, pub) || testEq(secret, priv) {
		t.Fatalf("GenerateECKeyPair returned identical secret and pub/priv keys this can't possibly happen:\npub: %v, priv: %v, secret: %v", pub, priv, secret)
	}
}

func TestNewMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	actual := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	if !testEq(actual.IPv4, publicip) || !testEq(actual.IPv6, publicip6) || actual.Port != publicport || !testEq(actual.PrivateIP, privateIP) || !testEq(actual.PublicKey, publicKey) {
		t.Fatalf("NewMapping did not return the right value, got: %v", actual)
	}
}

func TestParseMapping(t *testing.T) {
	privateIP := net.ParseIP("0.0.0.0")
	publicip := net.ParseIP("1.1.1.1")
	publicip6 := net.ParseIP("dead::beef")
	publicport := 80
	publicKey := make([]byte, 32)
	machineID := "123456"

	expected := NewMapping(machineID, privateIP, publicip, publicip6, publicport, publicKey)
	actual, err := ParseMapping(expected.String(), make([]byte, 32))
	if err != nil {
		t.Fatalf("Error occured during test: %s", err)
	}
	if !testEq(actual.IPv4, expected.IPv4) || actual.Port != expected.Port || !testEq(actual.PrivateIP, expected.PrivateIP) || !testEq(actual.PublicKey, expected.PublicKey) {
		t.Fatalf("ParseMapping did not return the right value, got: %v, expected: %v", actual, expected)
	}
}

func TestParseNetworkConfig(t *testing.T) {
	actual, err := ParseNetworkConfig(DefaultNetworkConfig.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != DefaultNetworkConfig.Network || actual.LeaseTime != DefaultNetworkConfig.LeaseTime {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, DefaultNetworkConfig)
	}
}

func TestParseNetworkConfigOnlyNetwork(t *testing.T) {
	netCfg := &NetworkConfig{Network: "10.10.0.0/16"}
	actual, err := ParseNetworkConfig(netCfg.Bytes())
	if err != nil {
		t.Fatal("ParseNetworkConfig returned an error:", err)
	}
	if actual.Network != netCfg.Network || actual.LeaseTime != 48*time.Hour {
		t.Fatalf("ParseNetworkConfig returned the wrong value, got: %v, expected: %v", actual, netCfg)
	}
}

func TestParseNetworkConfigIncorrectFormat(t *testing.T) {
	netCfg := &NetworkConfig{Network: "10.10.0."}
	_, err := ParseNetworkConfig(netCfg.Bytes())
	if err == nil {
		t.Fatal("ParseNetworkConfig should have errored")
	}

	netCfg.Network = "10.10.0.0/16"
	netCfg.StaticRange = "10.10.0./23"

	_, err = ParseNetworkConfig(netCfg.Bytes())
	if err == nil {
		t.Fatal("ParseNetworkConfig should have errored")
	}

	netCfg.StaticRange = "10.20.0.0/23"
	_, err = ParseNetworkConfig(netCfg.Bytes())
	if err == nil {
		t.Fatal("ParseNetworkConfig should have errored")
	}
}

func TestNewTunPayload(t *testing.T) {
	payload := NewTunPayload(testPacket, 2)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewSockPayload(t *testing.T) {
	payload := NewSockPayload(testPacket, 18)
	for i := 0; i < 4; i++ {
		if payload.IPAddress[i] != 1 {
			t.Fatal("NewTunPayload returned an incorrect IP address mapping.")
		}
	}

	for i := 0; i < 12; i++ {
		if payload.Nonce[i] != 2 {
			t.Fatal("NewTunPayload returned an incorrect Nonce mapping.")
		}
	}

	for i := 0; i < 2; i++ {
		if payload.Packet[i] != 3 {
			t.Fatal("NewTunPayload returned an incorrect Packet mapping.")
		}
	}
}

func TestNewStats(t *testing.T) {
	stats := NewStats(1)
	if stats.Packets != 0 {
		t.Fatalf("NewStats did not return the correct default for Packets, got: %d, expected: %d", stats.Packets, 0)
	}
	if stats.Bytes != 0 {
		t.Fatalf("NewStats did not return the correct default for Bytes, got: %d, expected: %d", stats.Bytes, 0)
	}
	if stats.Links == nil {
		t.Fatalf("NewStats did not return the correct default for Links, got: %v, expected: %v", stats.Links, make(map[string]*Stats))
	}
	str := stats.String()
	if str == "" {
		t.Fatalf("String didn't return the correct value.")
	}
}

func TestNewLogger(t *testing.T) {
	log := NewLogger()
	if log.Error == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
	if log.Info == nil {
		t.Fatal("NewLogger returned a nil Error log.")
	}
}

func TestGenerateLocalMapping(t *testing.T) {
	cfg := &Config{
		PrivateIP:     net.ParseIP("10.10.0.1"),
		PublicIPv4:    net.ParseIP("192.167.0.1"),
		PublicIPv6:    net.ParseIP("fd00:dead:beef::2"),
		ListenPort:    1099,
		PublicKey:     make([]byte, 32),
		NetworkConfig: DefaultNetworkConfig,
		MachineID:     "123",
	}

	mappings := make(map[uint32]*Mapping)
	mapping, err := GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	if !testEq(mapping.PrivateIP.To4(), cfg.PrivateIP.To4()) {
		t.Fatal("GenerateLocalMapping created the wrong mapping.")
	}

	mappings[IPtoInt(cfg.PrivateIP)] = mapping

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	mapping.MachineID = "456"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err == nil {
		t.Fatal("GenerateLocalMapping failed to properly handle an existing ip address")
	}

	cfg.PrivateIP = nil
	mapping.MachineID = "123"

	_, err = GenerateLocalMapping(cfg, mappings)
	if err != nil {
		t.Fatal(err)
	}

	cfg.PrivateIP = nil
	_, err = GenerateLocalMapping(cfg, make(map[uint32]*Mapping))
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 controller

import (
	"strconv"
	"strings"

	//"github.com/webx-top/blog/app/blog/lib"
	E "github.com/webx-top/echo"
	X "github.com/webx-top/webx"
	C "github.com/webx-top/webx/lib/captcha"
)

type Captcha struct {
	*Base
	show   X.Mapper `webx:":name"`
	reload X.Mapper `webx:".JSON"`
}

func (a *Captcha) Init(c *X.Context) error {
	a.Base = New(c)
	return nil
}

func (this *Captcha) Show() error {
	defer func() {
		this.Exit()
	}()
	if _, ok := this.checkRefer(func() error { return nil }); !ok {
		return nil
	}
	param := this.P(0)
	var id, ext string
	if p := strings.LastIndex(param, `.`); p > 0 {
		id = param[0:p]
		ext = param[p:]
	}
	if ext == "" || id == "" {
		return this.NotFound()
	}
	if this.Query("reload") != "" {
		C.Reload(id)
	}
	header := this.Response().Header()
	download := this.Query("download") != ""
	if download {
		header.Set(E.ContentType, "application/octet-stream")
	}
	switch ext {
	case ".png":
		if !download {
			header.Set(E.ContentType, "image/png")
		}
		return C.WriteImage(this.Response().Writer(), id, C.StdWidth, C.StdHeight)
	case ".wav":
		lang := strings.ToLower(this.Query("lang"))
		if lang != `en` && lang != `ru` && lang != `zh` {
			lang = `zh`
		}
		au, err := C.GetAudio(id, lang)
		if err != nil {
			return err
		}
		if !download {
			header.Set(E.ContentType, "audio/x-wav")
		}
		header.Set("Content-Length", strconv.Itoa(au.EncodedLen()))
		_, err = au.WriteTo(this.Response().Writer())
		return err
	}
	return nil
}

func (this *Captcha) Reload() (err error) {
	err, _ = this.checkRefer(func() error {
		d := struct{ Id string }{Id: C.New()}
		this.Output.Data = d
		return nil
	})
	return this.Display()
}

func (this *Captcha) checkAllowedDomain(refer string, allowedUrl string) bool {
	if allowedUrl == "" {
		return false
	}
	return strings.HasPrefix(refer, allowedUrl)
}

func (this *Captcha) checkRefer(f func() error) (err error, ret bool) {
	app := this.Query("app")
	allowedDomain := ""
	if app != "" {
		if domain := this.Server.App(app).Domain; domain != `` {
			allowedDomain = "http://" + domain
			this.Response().Header().Set("Access-Control-Allow-Origin", allowedDomain)
		}
	}
	//this.SetHeader("Access-Control-Allow-Origin", "*")
	r := this.Refer()
	logger := this.Server.Core.Logger()
	//println("[Refer]", r, this.IsAjax(), this.Request.Host)
	if r == "" || (strings.Contains(r, "://") && !this.checkAllowedDomain(r, allowedDomain) && !strings.Contains(r, "://"+this.Request().Host()+"/")) {
		logger.Errorf("[IP:%s]Update captcha from [%s] => Denied!", this.IP(), r)
		err = this.NotFound()
	} else {
		err = f()
		logger.Infof("[IP:%s]Update captcha from [%s] => Allowed.", this.IP(), r)
		ret = true
	}
	return
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 controller

import (
	"strconv"
	"strings"

	//"github.com/webx-top/blog/app/blog/lib"
	E "github.com/webx-top/echo"
	X "github.com/webx-top/webx"
	C "github.com/webx-top/webx/lib/captcha"
)

type Captcha struct {
	*Base
	show   X.Mapper `webx:":name"`
	reload X.Mapper `webx:".JSON"`
}

func (a *Captcha) Init(c *X.Context) error {
	a.Base = New(c)
	return nil
}

func (this *Captcha) Show() error {
	defer func() {
		this.Exit()
	}()
	if _, ok := this.checkRefer(func() error { return nil }); !ok {
		return nil
	}
	param := this.P(0)
	var id, ext string
	if p := strings.LastIndex(param, `.`); p > 0 {
		id = param[0:p]
		ext = param[p:]
	}
	if ext == "" || id == "" {
		return this.NotFound()
	}
	if this.Query("reload") != "" {
		C.Reload(id)
	}
	header := this.Response().Header()
	download := this.Query("download") != ""
	if download {
		header.Set(E.HeaderContentType, "application/octet-stream")
	}
	switch ext {
	case ".png":
		if !download {
			header.Set(E.HeaderContentType, "image/png")
		}
		return C.WriteImage(this.Response().Writer(), id, C.StdWidth, C.StdHeight)
	case ".wav":
		lang := strings.ToLower(this.Query("lang"))
		if lang != `en` && lang != `ru` && lang != `zh` {
			lang = `zh`
		}
		au, err := C.GetAudio(id, lang)
		if err != nil {
			return err
		}
		if !download {
			header.Set(E.HeaderContentType, "audio/x-wav")
		}
		header.Set("Content-Length", strconv.Itoa(au.EncodedLen()))
		_, err = au.WriteTo(this.Response().Writer())
		return err
	}
	return nil
}

func (this *Captcha) Reload() (err error) {
	err, _ = this.checkRefer(func() error {
		d := struct{ Id string }{Id: C.New()}
		this.Output.Data = d
		return nil
	})
	return this.Display()
}

func (this *Captcha) checkAllowedDomain(refer string, allowedUrl string) bool {
	if allowedUrl == "" {
		return false
	}
	return strings.HasPrefix(refer, allowedUrl)
}

func (this *Captcha) checkRefer(f func() error) (err error, ret bool) {
	app := this.Query("app")
	allowedDomain := ""
	if app != "" {
		if domain := this.Server.App(app).Domain; domain != `` {
			allowedDomain = "http://" + domain
			this.Response().Header().Set("Access-Control-Allow-Origin", allowedDomain)
		}
	}
	//this.SetHeader("Access-Control-Allow-Origin", "*")
	r := this.Refer()
	logger := this.Server.Core.Logger()
	//println("[Refer]", r, this.IsAjax(), this.Request.Host)
	if r == "" || (strings.Contains(r, "://") && !this.checkAllowedDomain(r, allowedDomain) && !strings.Contains(r, "://"+this.Request().Host()+"/")) {
		logger.Errorf("[IP:%s]Update captcha from [%s] => Denied!", this.IP(), r)
		err = this.NotFound()
	} else {
		err = f()
		logger.Infof("[IP:%s]Update captcha from [%s] => Allowed.", this.IP(), r)
		ret = true
	}
	return
}
<|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 fs

import (
	"fmt"
	"sort"
	"sync"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// State required for reading from directories.
type dirHandle struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	in           *inode.DirInode
	implicitDirs bool

	/////////////////////////
	// Mutable state
	/////////////////////////

	Mu syncutil.InvariantMutex

	// All entries in the directory. Populated the first time we need one.
	//
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	//
	// GUARDED_BY(Mu)
	entries []fuseutil.Dirent

	// Has entries yet been populated?
	//
	// INVARIANT: If !entriesValid, then len(entries) == 0
	//
	// GUARDED_BY(Mu)
	entriesValid bool
}

// Create a directory handle that obtains listings from the supplied inode.
func newDirHandle(
	in *inode.DirInode,
	implicitDirs bool) (dh *dirHandle) {
	// Set up the basic struct.
	dh = &dirHandle{
		in:           in,
		implicitDirs: implicitDirs,
	}

	// Set up invariant checking.
	dh.Mu = syncutil.NewInvariantMutex(dh.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Dirents, sorted by name.
type sortedDirents []fuseutil.Dirent

func (p sortedDirents) Len() int           { return len(p) }
func (p sortedDirents) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p sortedDirents) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func (dh *dirHandle) checkInvariants() {
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	for i := 0; i < len(dh.entries)-1; i++ {
		if !(dh.entries[i+1].Offset == dh.entries[i].Offset+1) {
			panic(
				fmt.Sprintf(
					"Unexpected offset sequence: %v, %v",
					dh.entries[i].Offset,
					dh.entries[i+1].Offset))
		}
	}

	// INVARIANT: If !entriesValid, then len(entries) == 0
	if !dh.entriesValid && len(dh.entries) != 0 {
		panic("Unexpected non-empty entries slice")
	}
}

// Read some entries from the directory inode. Return newTok == "" (possibly
// with a non-empty list of entries) when the end of the directory has been
// hit.
//
// The contents of the entries' Offset fields are undefined. This function
// always behaves as if implicit directories are defined; see notes on
// DirInode.ReadEntries.
//
// LOCKS_EXCLUDED(in)
func readSomeEntries(
	ctx context.Context,
	in *inode.DirInode,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	in.Lock()
	defer in.Unlock()

	entries, newTok, err = in.ReadEntries(ctx, tok)
	if err != nil {
		err = fmt.Errorf("ReadEntries: %v", err)
		return
	}

	// Return a bogus inode ID for each entry, but not the root inode ID.
	//
	// NOTE(jacobsa): As far as I can tell this is harmless. Minting and
	// returning a real inode ID is difficult because fuse does not count
	// readdir as an operation that increases the inode ID's lookup count and
	// we therefore don't get a forget for it later, but we would like to not
	// have to remember every inode ID that we've ever minted for readdir.
	//
	// If it turns out this is not harmless, we'll need to switch to something
	// like inode IDs based on (object name, generation) hashes. But then what
	// about the birthday problem? And more importantly, what about our
	// semantic of not minting a new inode ID when the generation changes due
	// to a local action?
	for i, _ := range entries {
		entries[i].Inode = fuseops.RootInodeID + 1
	}

	return
}

// Read all entries for the directory, making no effort to deal with
// conflicting names or the lack of implicit directories (see notes on
// DirInode.ReadEntries).
//
// Write entries to the supplied channel, without closing. Entry Offset fields
// have unspecified contents.
//
// LOCKS_EXCLUDED(in)
func readEntries(
	ctx context.Context,
	in *inode.DirInode,
	entries chan<- fuseutil.Dirent) (err error) {
	var tok string
	for {
		// Read a batch.
		var batch []fuseutil.Dirent

		batch, tok, err = readSomeEntries(ctx, in, tok)
		if err != nil {
			return
		}

		// Write each to the channel.
		for _, e := range batch {
			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case entries <- e:
			}
		}

		// Are we done?
		if tok == "" {
			break
		}
	}

	return
}

// Filter out entries with type DT_Directory for which in.LookUpChild does not
// return a directory. This can be used to implicit directories without a
// matching backing object from the output of DirInode.ReadDir, which always
// behaves as if implicit directories are enabled.
//
// LOCKS_EXCLUDED(in)
func filterMissingDirectories(
	ctx context.Context,
	in *inode.DirInode,
	entriesIn <-chan fuseutil.Dirent,
	entriesOut chan<- fuseutil.Dirent) (err error) {
	for e := range entriesIn {
		// If this is a directory, confirm it actually exists.
		if e.Type == fuseutil.DT_Directory {
			var o *gcs.Object
			in.Lock()
			o, err = in.LookUpChild(ctx, e.Name)
			defer in.Unlock()
			if err != nil {
				err = fmt.Errorf("LookUpChild: %v", err)
				return
			}

			// Skip this entry if the result is not an extant directory.
			if o == nil || !isDirName(o.Name) {
				continue
			}
		}

		// Pass on the entry.
		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case entriesOut <- e:
		}
	}

	return
}

// Resolve name conflicts between file objects and directory objects (e.g. the
// objects "foo/bar" and "foo/bar/") by appending U+000A, which is illegal in
// GCS object names, to conflicting file names.
//
// Input must be sorted by name.
func fixConflictingNames(entries []fuseutil.Dirent) (err error) {
	// Sanity check.
	if !sort.IsSorted(sortedDirents(entries)) {
		err = fmt.Errorf("Expected sorted input")
		return
	}

	// Examine each adjacent pair of names.
	for i, _ := range entries {
		e := &entries[i]

		// Find the previous entry.
		if i == 0 {
			continue
		}

		prev := &entries[i-1]

		// Does the pair have matching names?
		if e.Name != prev.Name {
			continue
		}

		// Repair whichever is the file, remembering that there's no way to get any
		// other type in the mix.
		if e.Type == fuseutil.DT_File {
			e.Name += inode.ConflictingFileNameSuffix
		} else {
			if prev.Type != fuseutil.DT_File {
				panic(fmt.Sprintf("Unexpected type for entry: %v", prev))
			}
			prev.Name += inode.ConflictingFileNameSuffix
		}
	}

	return
}

// LOCKS_EXCLUDED(dh.in)
func (dh *dirHandle) readAllEntries(
	ctx context.Context) (entries []fuseutil.Dirent, err error) {
	b := syncutil.NewBundle(ctx)

	// Read entries into a channel.
	unfiltered := make(chan fuseutil.Dirent, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(unfiltered)
		err = readEntries(ctx, dh.in, unfiltered)
		return
	})

	// If implicit directories are disabled, filter out entries for
	// sub-directories without a matching backing object.
	var filtered chan fuseutil.Dirent
	if dh.implicitDirs {
		filtered = unfiltered
	} else {
		filtered = make(chan fuseutil.Dirent, 100)

		var wg sync.WaitGroup
		f := func(ctx context.Context) error {
			defer wg.Done()
			return filterMissingDirectories(ctx, dh.in, unfiltered, filtered)
		}

		// Bound the parallelism with which we call the bucket.
		const filterWorkers = 32
		for i := 0; i < filterWorkers; i++ {
			wg.Add(1)
			b.Add(f)
		}

		go func() {
			wg.Wait()
			close(filtered)
		}()
	}

	// Accumulate entries into the slice.
	b.Add(func(ctx context.Context) (err error) {
		for e := range filtered {
			entries = append(entries, e)
		}

		return
	})

	// Wait.
	err = b.Join()
	if err != nil {
		return
	}

	// Ensure that the entries are sorted, for use in fixConflictingNames
	// below.
	sort.Sort(sortedDirents(entries))

	// Fix name conflicts.
	err = fixConflictingNames(entries)
	if err != nil {
		err = fmt.Errorf("fixConflictingNames: %v", err)
		return
	}

	// Fix up offset fields.
	for i := 0; i < len(entries); i++ {
		entries[i].Offset = fuseops.DirOffset(i) + 1
	}

	return
}

// LOCKS_REQUIRED(dh.Mu)
// LOCKS_EXCLUDED(dh.in)
func (dh *dirHandle) ensureEntries(ctx context.Context) (err error) {
	// Read entries.
	var entries []fuseutil.Dirent
	entries, err = dh.readAllEntries(ctx)
	if err != nil {
		err = fmt.Errorf("readAllEntries: %v", err)
		return
	}

	// Update state.
	dh.entries = entries
	dh.entriesValid = true

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Handle a request to read from the directory, without responding.
//
// Special case: we assume that a zero offset indicates that rewinddir has been
// called (since fuse gives us no way to intercept and know for sure), and
// start the listing process over again.
//
// LOCKS_REQUIRED(dh.Mu)
// LOCKS_EXCLUDED(du.in)
func (dh *dirHandle) ReadDir(
	op *fuseops.ReadDirOp) (err error) {
	// If the request is for offset zero, we assume that either this is the first
	// call or rewinddir has been called. Reset state.
	if op.Offset == 0 {
		dh.entries = nil
		dh.entriesValid = false
	}

	// Do we need to read entries from GCS?
	if !dh.entriesValid {
		err = dh.ensureEntries(op.Context())
		if err != nil {
			return
		}
	}

	// Is the offset past the end of what we have buffered? If so, this must be
	// an invalid seekdir according to posix.
	index := int(op.Offset)
	if index > len(dh.entries) {
		err = fuse.EINVAL
		return
	}

	// We copy out entries until we run out of entries or space.
	for i := index; i < len(dh.entries); i++ {
		op.Data = fuseutil.AppendDirent(op.Data, dh.entries[i])
		if len(op.Data) > op.Size {
			op.Data = op.Data[:op.Size]
			break
		}
	}

	return
}
<commit_msg>Update a comment.<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 fs

import (
	"fmt"
	"sort"
	"sync"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// State required for reading from directories.
type dirHandle struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	in           *inode.DirInode
	implicitDirs bool

	/////////////////////////
	// Mutable state
	/////////////////////////

	Mu syncutil.InvariantMutex

	// All entries in the directory. Populated the first time we need one.
	//
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	//
	// GUARDED_BY(Mu)
	entries []fuseutil.Dirent

	// Has entries yet been populated?
	//
	// INVARIANT: If !entriesValid, then len(entries) == 0
	//
	// GUARDED_BY(Mu)
	entriesValid bool
}

// Create a directory handle that obtains listings from the supplied inode.
func newDirHandle(
	in *inode.DirInode,
	implicitDirs bool) (dh *dirHandle) {
	// Set up the basic struct.
	dh = &dirHandle{
		in:           in,
		implicitDirs: implicitDirs,
	}

	// Set up invariant checking.
	dh.Mu = syncutil.NewInvariantMutex(dh.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Dirents, sorted by name.
type sortedDirents []fuseutil.Dirent

func (p sortedDirents) Len() int           { return len(p) }
func (p sortedDirents) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p sortedDirents) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func (dh *dirHandle) checkInvariants() {
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	for i := 0; i < len(dh.entries)-1; i++ {
		if !(dh.entries[i+1].Offset == dh.entries[i].Offset+1) {
			panic(
				fmt.Sprintf(
					"Unexpected offset sequence: %v, %v",
					dh.entries[i].Offset,
					dh.entries[i+1].Offset))
		}
	}

	// INVARIANT: If !entriesValid, then len(entries) == 0
	if !dh.entriesValid && len(dh.entries) != 0 {
		panic("Unexpected non-empty entries slice")
	}
}

// Read some entries from the directory inode. Return newTok == "" (possibly
// with a non-empty list of entries) when the end of the directory has been
// hit.
//
// The contents of the entries' Offset fields are undefined. This function
// always behaves as if implicit directories are defined; see notes on
// DirInode.ReadEntries.
//
// LOCKS_EXCLUDED(in)
func readSomeEntries(
	ctx context.Context,
	in *inode.DirInode,
	tok string) (entries []fuseutil.Dirent, newTok string, err error) {
	in.Lock()
	defer in.Unlock()

	entries, newTok, err = in.ReadEntries(ctx, tok)
	if err != nil {
		err = fmt.Errorf("ReadEntries: %v", err)
		return
	}

	// Return a bogus inode ID for each entry, but not the root inode ID.
	//
	// NOTE(jacobsa): As far as I can tell this is harmless. Minting and
	// returning a real inode ID is difficult because fuse does not count
	// readdir as an operation that increases the inode ID's lookup count and
	// we therefore don't get a forget for it later, but we would like to not
	// have to remember every inode ID that we've ever minted for readdir.
	//
	// If it turns out this is not harmless, we'll need to switch to something
	// like inode IDs based on (object name, generation) hashes. But then what
	// about the birthday problem? And more importantly, what about our
	// semantic of not minting a new inode ID when the generation changes due
	// to a local action?
	for i, _ := range entries {
		entries[i].Inode = fuseops.RootInodeID + 1
	}

	return
}

// Read all entries for the directory, making no effort to deal with
// conflicting names or the lack of implicit directories (see notes on
// DirInode.ReadEntries).
//
// Write entries to the supplied channel, without closing. Entry Offset fields
// have unspecified contents.
//
// LOCKS_EXCLUDED(in)
func readEntries(
	ctx context.Context,
	in *inode.DirInode,
	entries chan<- fuseutil.Dirent) (err error) {
	var tok string
	for {
		// Read a batch.
		var batch []fuseutil.Dirent

		batch, tok, err = readSomeEntries(ctx, in, tok)
		if err != nil {
			return
		}

		// Write each to the channel.
		for _, e := range batch {
			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case entries <- e:
			}
		}

		// Are we done?
		if tok == "" {
			break
		}
	}

	return
}

// Filter out entries with type DT_Directory for which in.LookUpChild does not
// return a directory. This can be used to implicit directories without a
// matching backing object from the output of DirInode.ReadDir, which always
// behaves as if implicit directories are enabled.
//
// LOCKS_EXCLUDED(in)
func filterMissingDirectories(
	ctx context.Context,
	in *inode.DirInode,
	entriesIn <-chan fuseutil.Dirent,
	entriesOut chan<- fuseutil.Dirent) (err error) {
	for e := range entriesIn {
		// If this is a directory, confirm it actually exists.
		if e.Type == fuseutil.DT_Directory {
			var o *gcs.Object
			in.Lock()
			o, err = in.LookUpChild(ctx, e.Name)
			defer in.Unlock()
			if err != nil {
				err = fmt.Errorf("LookUpChild: %v", err)
				return
			}

			// Skip this entry if the result is not an extant directory.
			if o == nil || !isDirName(o.Name) {
				continue
			}
		}

		// Pass on the entry.
		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case entriesOut <- e:
		}
	}

	return
}

// Resolve name conflicts between file objects and directory objects (e.g. the
// objects "foo/bar" and "foo/bar/") by appending U+000A, which is illegal in
// GCS object names, to conflicting file names.
//
// Input must be sorted by name.
func fixConflictingNames(entries []fuseutil.Dirent) (err error) {
	// Sanity check.
	if !sort.IsSorted(sortedDirents(entries)) {
		err = fmt.Errorf("Expected sorted input")
		return
	}

	// Examine each adjacent pair of names.
	for i, _ := range entries {
		e := &entries[i]

		// Find the previous entry.
		if i == 0 {
			continue
		}

		prev := &entries[i-1]

		// Does the pair have matching names?
		if e.Name != prev.Name {
			continue
		}

		// Repair whichever is the file, remembering that there's no way to get any
		// other type in the mix.
		if e.Type == fuseutil.DT_File {
			e.Name += inode.ConflictingFileNameSuffix
		} else {
			if prev.Type != fuseutil.DT_File {
				panic(fmt.Sprintf("Unexpected type for entry: %v", prev))
			}
			prev.Name += inode.ConflictingFileNameSuffix
		}
	}

	return
}

// LOCKS_EXCLUDED(dh.in)
func (dh *dirHandle) readAllEntries(
	ctx context.Context) (entries []fuseutil.Dirent, err error) {
	b := syncutil.NewBundle(ctx)

	// Read entries into a channel.
	unfiltered := make(chan fuseutil.Dirent, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(unfiltered)
		err = readEntries(ctx, dh.in, unfiltered)
		return
	})

	// If implicit directories are disabled, filter out entries for
	// sub-directories without a matching backing object.
	var filtered chan fuseutil.Dirent
	if dh.implicitDirs {
		filtered = unfiltered
	} else {
		filtered = make(chan fuseutil.Dirent, 100)

		var wg sync.WaitGroup
		f := func(ctx context.Context) error {
			defer wg.Done()
			return filterMissingDirectories(ctx, dh.in, unfiltered, filtered)
		}

		// Bound the parallelism with which we call the inode.
		const filterWorkers = 32
		for i := 0; i < filterWorkers; i++ {
			wg.Add(1)
			b.Add(f)
		}

		go func() {
			wg.Wait()
			close(filtered)
		}()
	}

	// Accumulate entries into the slice.
	b.Add(func(ctx context.Context) (err error) {
		for e := range filtered {
			entries = append(entries, e)
		}

		return
	})

	// Wait.
	err = b.Join()
	if err != nil {
		return
	}

	// Ensure that the entries are sorted, for use in fixConflictingNames
	// below.
	sort.Sort(sortedDirents(entries))

	// Fix name conflicts.
	err = fixConflictingNames(entries)
	if err != nil {
		err = fmt.Errorf("fixConflictingNames: %v", err)
		return
	}

	// Fix up offset fields.
	for i := 0; i < len(entries); i++ {
		entries[i].Offset = fuseops.DirOffset(i) + 1
	}

	return
}

// LOCKS_REQUIRED(dh.Mu)
// LOCKS_EXCLUDED(dh.in)
func (dh *dirHandle) ensureEntries(ctx context.Context) (err error) {
	// Read entries.
	var entries []fuseutil.Dirent
	entries, err = dh.readAllEntries(ctx)
	if err != nil {
		err = fmt.Errorf("readAllEntries: %v", err)
		return
	}

	// Update state.
	dh.entries = entries
	dh.entriesValid = true

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Handle a request to read from the directory, without responding.
//
// Special case: we assume that a zero offset indicates that rewinddir has been
// called (since fuse gives us no way to intercept and know for sure), and
// start the listing process over again.
//
// LOCKS_REQUIRED(dh.Mu)
// LOCKS_EXCLUDED(du.in)
func (dh *dirHandle) ReadDir(
	op *fuseops.ReadDirOp) (err error) {
	// If the request is for offset zero, we assume that either this is the first
	// call or rewinddir has been called. Reset state.
	if op.Offset == 0 {
		dh.entries = nil
		dh.entriesValid = false
	}

	// Do we need to read entries from GCS?
	if !dh.entriesValid {
		err = dh.ensureEntries(op.Context())
		if err != nil {
			return
		}
	}

	// Is the offset past the end of what we have buffered? If so, this must be
	// an invalid seekdir according to posix.
	index := int(op.Offset)
	if index > len(dh.entries) {
		err = fuse.EINVAL
		return
	}

	// We copy out entries until we run out of entries or space.
	for i := index; i < len(dh.entries); i++ {
		op.Data = fuseutil.AppendDirent(op.Data, dh.entries[i])
		if len(op.Data) > op.Size {
			op.Data = op.Data[:op.Size]
			break
		}
	}

	return
}
<|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 inode

import (
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// TODO(jacobsa): Add a Destroy method here that calls ObjectProxy.Destroy, and
// make sure it's called when the inode is forgotten. Also, make sure package
// fuse has support for actually calling Forget.
type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id   fuse.InodeID
	name string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// A proxy for the backing object in GCS.
	//
	// INVARIANT: proxy.CheckInvariants() does not panic
	//
	// GUARDED_BY(mu)
	proxy *gcsproxy.ObjectProxy
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS.
//
// REQUIRES: o != nil
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	bucket gcs.Bucket,
	id fuse.InodeID,
	o *storage.Object) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket:    bucket,
		id:        id,
		name:      o.Name,
		srcObject: *o,
	}

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (f *FileInode) checkInvariants() {
	if len(f.name) == 0 || f.name[len(f.name)-1] == '/' {
		panic("Illegal file name: " + f.name)
	}
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuse.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuse.InodeAttributes, err error) {
	attrs = fuse.InodeAttributes{
		Size:  uint64(f.srcObject.Size),
		Mode:  0700,
		Mtime: f.srcObject.Updated,
	}

	return
}

// Return the generation number from which this inode was branched. This is
// used as a precondition in object write requests.
//
// TODO(jacobsa): Make sure to add a test for opening a file with O_CREAT then
// opening it again for reading, and sharing data across the two descriptors.
// This should fail if we have screwed up the fuse lookup process with regards
// to the zero generation.
//
// SHARED_LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGeneration() int64 {
	return f.srcObject.Generation
}
<commit_msg>Updated FileInode.checkInvariants.<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 inode

import (
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// TODO(jacobsa): Add a Destroy method here that calls ObjectProxy.Destroy, and
// make sure it's called when the inode is forgotten. Also, make sure package
// fuse has support for actually calling Forget.
type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id   fuse.InodeID
	name string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// A proxy for the backing object in GCS.
	//
	// INVARIANT: proxy.CheckInvariants() does not panic
	//
	// GUARDED_BY(mu)
	proxy *gcsproxy.ObjectProxy
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS.
//
// REQUIRES: o != nil
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	bucket gcs.Bucket,
	id fuse.InodeID,
	o *storage.Object) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket:    bucket,
		id:        id,
		name:      o.Name,
		srcObject: *o,
	}

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (f *FileInode) checkInvariants() {
	// Make sure the name is legal.
	if len(f.name) == 0 || f.name[len(f.name)-1] == '/' {
		panic("Illegal file name: " + f.name)
	}

	// INVARIANT: proxy.CheckInvariants() does not panic
	f.proxy.CheckInvariants()
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuse.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuse.InodeAttributes, err error) {
	attrs = fuse.InodeAttributes{
		Size:  uint64(f.srcObject.Size),
		Mode:  0700,
		Mtime: f.srcObject.Updated,
	}

	return
}

// Return the generation number from which this inode was branched. This is
// used as a precondition in object write requests.
//
// TODO(jacobsa): Make sure to add a test for opening a file with O_CREAT then
// opening it again for reading, and sharing data across the two descriptors.
// This should fail if we have screwed up the fuse lookup process with regards
// to the zero generation.
//
// SHARED_LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGeneration() int64 {
	return f.srcObject.Generation
}
<|endoftext|>


<commit_before>package memfs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"unicode"
)

// Box drawings symbols - http://unicode-table.com/en/sections/box-drawing/.
var (
	boxVerticalRight = []byte("├")
	boxHorizontal    = []byte("─")
	boxVertical      = []byte("│")
	boxUpRight       = []byte("└")
	boxSpace         = []byte{'\u0020'}
	boxHardSpace     = []byte{'\u00A0'}
)

func max(i, j int) int {
	if i > j {
		return i
	}
	return j
}

// CustomTree instructs tree builder how to parse single line of given buffer,
// where 'name' is the name of a tree node, 'depth' is its depth in the tree
// and 'err' eventual parsing failure. The 'line' is guaranteed to be non-nil
// non-empty.
type CustomTree func(line []byte) (depth int, name []byte, err error)

// Unix is a tree builder for the 'tree' Unix command.
var Unix CustomTree

// Tab is a tree builder for simplified tree representation, where each level
// is idented with one tabulation character (\t) only.
var Tab CustomTree

func init() {
	Unix = func(p []byte) (depth int, name []byte, err error) {
		var n int
		// TODO(rjeczalik): Count up to first non-box character.
		depth = (bytes.Count(p, boxSpace) + bytes.Count(p, boxHardSpace) +
			bytes.Count(p, boxVertical)) / 4
		if n = bytes.LastIndex(p, boxHorizontal); n == -1 {
			err = fmt.Errorf("invalid syntax: %q", p)
			return
		}
		name = p[n:]
		if n = bytes.Index(name, boxSpace); n == -1 {
			err = fmt.Errorf("invalid syntax: %q", p)
			return
		}
		name = name[n+1:]
		return
	}
	Tab = func(p []byte) (depth int, name []byte, err error) {
		depth = bytes.Count(p, []byte{'\t'})
		name = p[depth:]
		return
	}
}

// Tree builds FS.Tree from given reader using CustomTree callback for parsing
// node's name and its depth in the tree.
func (ct CustomTree) Tree(r io.Reader) (fs FS, err error) {
	var (
		dir       = Directory{}
		buf       = bufio.NewReader(r)
		glob      []Directory
		name      []byte
		prevName  []byte
		depth     int
		prevDepth int
	)
	fs.Tree = dir
	line, err := buf.ReadBytes('\n')
	if len(line) == 0 || err == io.EOF {
		err = io.ErrUnexpectedEOF
		return
	}
	if err != nil {
		return
	}
	if len(line) != 1 || line[0] != '.' {
		p := filepath.FromSlash(string(bytes.TrimSpace(line)))
		if err = fs.MkdirAll(p, 0); err != nil {
			return
		}
		// TODO(rjeczalik): make it an exported helper method
		var perr *os.PathError
		if dir, perr = fs.lookup(p); perr != nil {
			err = perr
			return
		}
	}
	glob = append(glob, dir)
	for {
		line, err = buf.ReadBytes('\n')
		if len(bytes.TrimSpace(line)) == 0 {
			io.Copy(ioutil.Discard, buf)
			err, line = io.EOF, nil
		} else {
			depth, name, err = ct(bytes.TrimRightFunc(line, unicode.IsSpace))
		}
		// Skip first iteration.
		if len(prevName) != 0 {
			// Insert the node from previous iteration - node is a directory when
			// a diference of the tree depth > 0, a file otherwise.
			var (
				name  string
				value interface{}
			)
			if bytes.HasSuffix(prevName, []byte{'/'}) {
				name, value = string(bytes.TrimRight(prevName, "/")), Directory{}
			} else {
				name, value = string(prevName), File{}
			}
			switch {
			case depth > prevDepth:
				d := Directory{}
				dir[name], glob, dir = d, append(glob, dir), d
			case depth == prevDepth:
				dir[name] = value
			case depth < prevDepth:
				n := max(len(glob)+depth-prevDepth, 0)
				dir[name], dir, glob = value, glob[n], glob[:n]
			}
		}
		// A node from each iteration is handled on the next one. That's why the
		// error handling is deferred.
		if len(line) == 0 {
			if err == io.EOF {
				err = nil
			}
			return
		}
		prevDepth, prevName = depth, name
	}
}

// UnixTree builds FS.Tree from a buffer that contains tree-like (Unix command) output.
//
// Example:
//
//   var tree = []byte(`.
//   └── dir
//       └── file.txt`)
//
//   fs, _ = memfs.UnixTree(tree)
//   fmt.Printf("%#v\n", fs)
//
//   // Produces:
//   // memfs.FS{Tree: memfs.Directory{"dir": memfs.Directory{"file": memfs.File{}}}}
//
// UnixTree(p) is an short alternative to the Unix.Tree(bytes.NewReader(p)).
func UnixTree(p []byte) (FS, error) {
	return Unix.Tree(bytes.NewReader(p))
}

// TabTree builds FS.Tree from a buffer that contains \t-separated file tree.
//
// Example:
//
//   var tree = []byte(`.
//   dir
//   	file.txt`)
//
//   fs, _ = memfs.TabTree(tree)
//   fmt.Printf("%#v\n", fs)
//   // Produces:
//   // memfs.FS{Tree: memfs.Directory{"dir": memfs.Directory{"file": memfs.File{}}}}
//
// TabTree(p) is an short alternative to the Tab.Tree(bytes.NewReader(p)).
func TabTree(p []byte) (FS, error) {
	return Tab.Tree(bytes.NewReader(p))
}
<commit_msg>fs/memfs: Update inline documentation<commit_after>package memfs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"unicode"
)

// Box drawings symbols - http://unicode-table.com/en/sections/box-drawing/.
var (
	boxVerticalRight = []byte("├")
	boxHorizontal    = []byte("─")
	boxVertical      = []byte("│")
	boxUpRight       = []byte("└")
	boxSpace         = []byte{'\u0020'}
	boxHardSpace     = []byte{'\u00A0'}
)

func max(i, j int) int {
	if i > j {
		return i
	}
	return j
}

// CustomTree instructs tree builder how to parse single line of given buffer,
// where 'name' is the name of a tree node, 'depth' is its depth in the tree
// and 'err' eventual parsing failure. The 'line' is guaranteed to be non-nil
// non-empty.
type CustomTree func(line []byte) (depth int, name []byte, err error)

// Unix is a tree builder for the 'tree' Unix command.
var Unix CustomTree

// Tab is a tree builder for simplified tree representation, where each level
// is idented with one tabulation character (\t) only.
var Tab CustomTree

func init() {
	Unix = func(p []byte) (depth int, name []byte, err error) {
		var n int
		// TODO(rjeczalik): Count up to first non-box character.
		depth = (bytes.Count(p, boxSpace) + bytes.Count(p, boxHardSpace) +
			bytes.Count(p, boxVertical)) / 4
		if n = bytes.LastIndex(p, boxHorizontal); n == -1 {
			err = fmt.Errorf("invalid syntax: %q", p)
			return
		}
		name = p[n:]
		if n = bytes.Index(name, boxSpace); n == -1 {
			err = fmt.Errorf("invalid syntax: %q", p)
			return
		}
		name = name[n+1:]
		return
	}
	Tab = func(p []byte) (depth int, name []byte, err error) {
		depth = bytes.Count(p, []byte{'\t'})
		name = p[depth:]
		return
	}
}

// Tree builds FS.Tree from given reader using CustomTree callback for parsing
// node's name and its depth in the tree.
func (ct CustomTree) Tree(r io.Reader) (fs FS, err error) {
	var (
		dir       = Directory{}
		buf       = bufio.NewReader(r)
		glob      []Directory
		name      []byte
		prevName  []byte
		depth     int
		prevDepth int
	)
	fs.Tree = dir
	line, err := buf.ReadBytes('\n')
	if len(line) == 0 || err == io.EOF {
		err = io.ErrUnexpectedEOF
		return
	}
	if err != nil {
		return
	}
	if len(line) != 1 || line[0] != '.' {
		p := filepath.FromSlash(string(bytes.TrimSpace(line)))
		if err = fs.MkdirAll(p, 0); err != nil {
			return
		}
		// TODO(rjeczalik): make it an exported helper method
		var perr *os.PathError
		if dir, perr = fs.lookup(p); perr != nil {
			err = perr
			return
		}
	}
	glob = append(glob, dir)
	for {
		line, err = buf.ReadBytes('\n')
		if len(bytes.TrimSpace(line)) == 0 {
			io.Copy(ioutil.Discard, buf)
			err, line = io.EOF, nil
		} else {
			depth, name, err = ct(bytes.TrimRightFunc(line, unicode.IsSpace))
		}
		// Skip first iteration.
		if len(prevName) != 0 {
			// Insert the node from previous iteration - node is a directory when
			// a diference of the tree depth > 0, a file otherwise.
			var (
				name  string
				value interface{}
			)
			if bytes.HasSuffix(prevName, []byte{'/'}) {
				name, value = string(bytes.TrimRight(prevName, "/")), Directory{}
			} else {
				name, value = string(prevName), File{}
			}
			switch {
			case depth > prevDepth:
				d := Directory{}
				dir[name], glob, dir = d, append(glob, dir), d
			case depth == prevDepth:
				dir[name] = value
			case depth < prevDepth:
				n := max(len(glob)+depth-prevDepth, 0)
				dir[name], dir, glob = value, glob[n], glob[:n]
			}
		}
		// A node from each iteration is handled on the next one. That's why the
		// error handling is deferred.
		if len(line) == 0 {
			if err == io.EOF {
				err = nil
			}
			return
		}
		prevDepth, prevName = depth, name
	}
}

// UnixTree builds FS.Tree from a buffer that contains tree-like (Unix command) output.
//
// Example:
//
//   var tree = []byte(`.
//   └── dir
//       └── file.txt`)
//
//   var fs = memfs.Must(memfs.UnixTree(tree))
//
// The above is an equivalent to:
//
//   var fs = memfs.FS{
//              Tree: memfs.Directory{
//                "dir": memfs.Directory{
//                  "file.txt": memfs.File{},
//                },
//              },
//            }
//
// UnixTree(p) is a short alternative to the Unix.Tree(bytes.NewReader(p)).
func UnixTree(p []byte) (FS, error) {
	return Unix.Tree(bytes.NewReader(p))
}

// TabTree builds FS.Tree from a buffer that contains \t-separated file tree.
//
// Example:
//
//   var tree = []byte(`.\ndir\n\tfile1.txt\n\tfile2.txt`)
//   var fs = memfs.Must(memfs.TabTree(tree))
//
// The above is an equivalent to:
//
//   var fs = memfs.FS{
//              Tree: memfs.Directory{
//                "dir": memfs.Directory{
//                  "file1.txt": memfs.File{},
//                  "file2.txt": memfs.File{},
//                },
//              },
//            }
//
// TabTree(p) is a short alternative to the Tab.Tree(bytes.NewReader(p)).
func TabTree(p []byte) (FS, error) {
	return Tab.Tree(bytes.NewReader(p))
}
<|endoftext|>