aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/cmd.go57
-rw-r--r--cmd/errors.go42
-rw-r--r--cmd/flag.go154
-rw-r--r--cmd/format.go0
-rw-r--r--cmd/helper.go20
-rw-r--r--cmd/info.go72
-rw-r--r--cmd/output.go201
-rw-r--r--cmd/strings.go34
-rw-r--r--cmd/templates.go0
-rw-r--r--cmd/version.go62
-rw-r--r--ext4/ext4.go35
-rw-r--r--util/users.go50
-rw-r--r--util/util.go11
13 files changed, 711 insertions, 27 deletions
diff --git a/cmd/cmd.go b/cmd/cmd.go
index e2f0cf4..725aaea 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -17,34 +17,59 @@
* the License.
*/
-// Package cmd is the common library for writing fscrypt command line binaries.
+// Package cmd is the common library for writing command line binaries.
// This package is mainly a wrapper around github.com/urfave/cli, but provides
// additional support to make the usage look similar to the man page.
//
-// The main componets are the `Cmd` and `Flag` types which can be used to define
-// a top-level command with many potential subcommands. This package also
-// presents a smaller interface than urfave/cli, making it easier to use for
-// other commands.
+// The main componets are the `Cmd`, `Argument`, and `Flag` types which can be
+// used to define a top-level command with many potential subcommands. This
+// package also presents a smaller interface than urfave/cli, making it easier
+// to use for other commands.
package cmd
+import "os"
+
// Command represents a command with many potential top-level commands. This is
-// trand
-type Cmd struct {
- Name string
+// transformed into a cli.Command in Run().
+type Command struct {
+ Name string
UsageLines []string
- SubCmds []Cmd
- Arguments []Argument
- Flags []cli.Flag
- Man *ManEntry
- Action CommandFunc
+ SubCmds []*Command
+ Arguments []*Argument
+ Flags []Flag
+ ManPage *ManEntry
+ Action CommandFunc
}
+// Argument represents a parameter passed to a function. It has an optional
+// usage explains how it should be used.
type Argument struct {
- Name string
- Usage string
+ ArgName string
+ Usage string
}
+// ManEntry represents an entry in a man page with a name, section, and title.
type ManEntry struct {
- Title string
+ Name string
Section int
+ Title string
+}
+
+// CommandFunc contains the implementation of a command. The provided args have
+// the flags and leading command names removed. If a normal error is returned,
+// it is printed out (with an optional explanation) and exits with FailureCode.
+// If a usage error is returned, it is printed out with the command's usage and
+// exits with UsageFailureCode. Returning nil causes an exit with success.
+type CommandFunc func(args []string) error
+
+// Run executes the command with os.Args, equivalent to c.RunArgs(os.Args).
+func (c *Command) Run() {
+ c.RunArgs(os.Args)
+}
+
+// RunArgs executes the command with the provided args. If the Name argument is
+// empty, args[0]'s basename is used instead. If the command fails, this method
+// will not return.
+func (c *Command) RunArgs(args []string) {
+ // TODO(joerichey): Implement conversion to cli.Command
}
diff --git a/cmd/errors.go b/cmd/errors.go
new file mode 100644
index 0000000..07a1d05
--- /dev/null
+++ b/cmd/errors.go
@@ -0,0 +1,42 @@
+/*
+ * errors.go - Common errors and error handling
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+import (
+ "github.com/pkg/errors"
+
+ "github.com/google/fscrypt/util"
+)
+
+// Common errors used across commands
+var (
+ ErrUnknownVersion = errors.New("unknown (missing version tag)")
+ ErrCanceled = errors.New("operation canceled by user")
+ ErrMustForce = errors.New("operation must be forced")
+ ErrNotRoot = errors.New("operation must be run as root")
+)
+
+// CheckIfRoot returns an error if the current user is not the root user.
+func CheckIfRoot() error {
+ if id := util.CurrentUserID(); id != 0 {
+ return errors.Wrapf(ErrNotRoot, "user %s", util.GetUser(id).Username)
+ }
+ return nil
+}
diff --git a/cmd/flag.go b/cmd/flag.go
new file mode 100644
index 0000000..18b2a4c
--- /dev/null
+++ b/cmd/flag.go
@@ -0,0 +1,154 @@
+/*
+ * flag.go - Definitions for flags and associated formatting functions.
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+import (
+ "flag"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// Flag represents a command line flag that can be passed to a command. Note
+// that Flag also conforms to the cli.Flag interface. The Name, ArgName, and
+// Usage of the Flag can be used to format it in a short form with ShortFormat,
+// or in it's full format with the String method.
+type Flag interface {
+ fmt.Stringer
+ Apply(*flag.FlagSet)
+ GetName() string
+ GetArgName() string
+ GetUsage() string
+}
+
+// How the first usage line for a Flag should appear. We have two formats:
+// --name
+// --name=<argName>
+// The <argName> appears if the prettyFlag's GetArgName() method returns a
+// non-empty string. The returned string from shortFormat() does not include
+// any leading or trailing whitespace.
+func ShortFormat(f Flag) string {
+ if argName := f.GetArgName(); argName != "" {
+ return fmt.Sprintf("--%s=%s", f.GetName(), argName)
+ }
+ return fmt.Sprintf("--%s", f.GetName())
+}
+
+// How our flags should appear when displaying their usage. An example would be:
+// --help
+// Prints help screen for commands and subcommands.
+//
+// If defaultString is specified, this if appended to the usage. Example:
+//
+// --legacy
+// Allow for support of older kernels with ext4 (before v4.8) and
+// F2FS (before v4.6) filesystems. (default: true)
+func longFormat(f Flag, defaultString ...string) string {
+ usage := f.GetUsage()
+ if len(defaultString) > 0 {
+ usage += fmt.Sprintf(" (default: %v)", defaultString[0])
+ }
+
+ usage = wrapText(usage, 2)
+ return fmt.Sprintf("\t%s\n%s", ShortFormat(f), usage)
+}
+
+// BoolFlag is a Flag of type bool.
+type BoolFlag struct {
+ Name string
+ Usage string
+ Default bool
+ Value bool
+}
+
+func (f *BoolFlag) String() string {
+ if !f.Default {
+ return longFormat(f)
+ }
+ return longFormat(f, strconv.FormatBool(f.Default))
+}
+
+// Apply uses BoolFlag's value to set a flag.BoolVar on the FlagSet.
+func (f *BoolFlag) Apply(s *flag.FlagSet) { s.BoolVar(&f.Value, f.Name, f.Default, f.Usage) }
+
+// GetName just returns BoolFlag's name.
+func (f *BoolFlag) GetName() string { return f.Name }
+
+// GetArgName returns nothing as BoolFlags don't have an argument name.
+func (f *BoolFlag) GetArgName() string { return "" }
+
+// GetUsage returns BoolFlag's usage.
+func (f *BoolFlag) GetUsage() string { return f.Usage }
+
+// StringFlag is a Flag of type string.
+type StringFlag struct {
+ Name string
+ ArgName string
+ Usage string
+ Default string
+ Value string
+}
+
+func (f *StringFlag) String() string {
+ if f.Default == "" {
+ return longFormat(f)
+ }
+ return longFormat(f, strconv.Quote(f.Default))
+}
+
+// Apply uses StringFlag's value to set a flag.StringVar on the FlagSet.
+func (f *StringFlag) Apply(s *flag.FlagSet) { s.StringVar(&f.Value, f.Name, f.Default, f.Usage) }
+
+// GetName just returns StringFlag's name.
+func (f *StringFlag) GetName() string { return f.Name }
+
+// GetArgName returns StringFlag's argument name.
+func (f *StringFlag) GetArgName() string { return f.ArgName }
+
+// GetUsage returns StringFlag's usage.
+func (f *StringFlag) GetUsage() string { return f.Usage }
+
+// DurationFlag is a Flag of type time.Duration.
+type DurationFlag struct {
+ Name string
+ ArgName string
+ Usage string
+ Default time.Duration
+ Value time.Duration
+}
+
+func (f *DurationFlag) String() string {
+ if f.Default == 0 {
+ return longFormat(f)
+ }
+ return longFormat(f, f.Default.String())
+}
+
+// Apply uses DurationFlag's value to set a flag.DurationVar on the FlagSet.
+func (f *DurationFlag) Apply(s *flag.FlagSet) { s.DurationVar(&f.Value, f.Name, f.Default, f.Usage) }
+
+// GetName just returns DurationFlag's name.
+func (f *DurationFlag) GetName() string { return f.Name }
+
+// GetArgName returns DurationFlag's argument name.
+func (f *DurationFlag) GetArgName() string { return f.ArgName }
+
+// GetUsage returns DurationFlag's usage.
+func (f *DurationFlag) GetUsage() string { return f.Usage }
diff --git a/cmd/format.go b/cmd/format.go
deleted file mode 100644
index e69de29..0000000
--- a/cmd/format.go
+++ /dev/null
diff --git a/cmd/helper.go b/cmd/helper.go
index e69de29..77f6b2f 100644
--- a/cmd/helper.go
+++ b/cmd/helper.go
@@ -0,0 +1,20 @@
+/*
+ * helper.go - Helper functions for using the cmd package
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
diff --git a/cmd/info.go b/cmd/info.go
new file mode 100644
index 0000000..6257ec1
--- /dev/null
+++ b/cmd/info.go
@@ -0,0 +1,72 @@
+/*
+ * info.go - Global information about the program.
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+import (
+ "time"
+
+ "github.com/urfave/cli"
+)
+
+// Info contains the global info for the functions.
+var Info struct {
+ // Program is the name of the top-level program being executed. If not
+ // set it is set in cmd.RunArgs().
+ Program string
+ // VersionTag (if set) will be displayed in both the short and long
+ // version output. VersionTag is not parsed, so any string will work.
+ VersionTag string
+ // BuildTime (if set) will be displayed in the long version output.
+ BuildTime time.Time
+ // Authors (if non-empty) are displayed in the long version output.
+ Authors []cli.Author
+ // Copyright (if set) is displayed in the long version output.
+ Copyright string
+}
+
+// Linker flags of the form "-X cmd.Info.VersionTag=1.0" do not work, so we use
+// these separate files so variables can be set from the Makefile.
+var (
+ versionTag string
+ buildTime string
+)
+
+// fscrypt specific initialization
+func init() {
+ Info.VersionTag = versionTag
+ Info.BuildTime = buildTime
+ Info.Authors = []cli.Author{{
+ Name: "Joe Richey",
+ Email: "joerichey@google.com",
+ }}
+ Info.Copyright = `Copyright 2017 Google, Inc.
+
+ 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.`
+}
diff --git a/cmd/output.go b/cmd/output.go
new file mode 100644
index 0000000..024705d
--- /dev/null
+++ b/cmd/output.go
@@ -0,0 +1,201 @@
+/*
+ * output.go - Functions for handling command line formatting and output.
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/google/fscrypt/util"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var (
+ // TabWidth is the number of spaces used to display a tab.
+ TabWidth = 8
+ // LineLength is the maximum length of any output. If not set, the width
+ // of the terminal be detected and assigned to LineLength.
+ LineLength int
+ // FallbackLineLength is the LineLength used if detection fails. By
+ // default we fall back to punch cards.
+ FallbackLineLength = 80
+ // MaxLineLength is the maximum allowed detected value of LineLength.
+ MaxLineLength = 120
+ // Output is the io.Writer all commands should use for their normal
+ // output (errors should just return the appropriate error). If not set,
+ // it is automatically set based on the provided flags.
+ Output io.Writer
+ // HelpFlag writes help to Stdout
+ HelpFlag = &BoolFlag{
+ Name: "help",
+ Usage: "Prints this 🧗help text for commands and subcommands",
+ }
+ // VerboseFlag indicates that all logging output should be printed.
+ VerboseFlag = &BoolFlag{
+ Name: "verbose",
+ Usage: "Prints additional debug messages.",
+ }
+ // QuietFlag indicates that no normal output should be printed.
+ QuietFlag = &BoolFlag{
+ Name: "quiet",
+ Usage: `Prints nothing except for errors and uses any default
+ option instead of prompting the user.`,
+ }
+ // ForceFlag indicates that the operation should proceed if possible.
+ ForceFlag = &BoolFlag{
+ Name: "force",
+ Usage: `Print no confirmation prompts or warnings and
+ automatically proceed with the requested action.`,
+ }
+)
+
+// Suffixes for questions with a yes or no default
+const (
+ defaultYesSuffix = "[Y/n]"
+ defaultNoSuffix = "[y/N]"
+)
+
+// We use the width of the terminal unless we cannot get the width.
+func init() {
+ if LineLength > 0 {
+ return
+ }
+ width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
+ if err != nil {
+ LineLength = FallbackLineLength
+ } else {
+ LineLength = util.MinInt(width, MaxLineLength)
+ }
+}
+
+// Takes an input string text, and wraps the text so that each line begins with
+// numTabs tabs and ends with a newline (except the last line), and each line
+// has length less than lineLength. If the text contains a word which is too
+// long, that word gets its own line.
+func wrapText(text string, numTabs int) string {
+ // We use a buffer to format the wrapped text so we get O(n) runtime
+ var buffer bytes.Buffer
+ spaceLeft := 0
+ maxTextLen := LineLength - numTabs*TabWidth
+ delimiter := strings.Repeat("\t", numTabs)
+ for i, word := range strings.Fields(text) {
+ wordLen := utf8.RuneCountInString(word)
+ if wordLen >= spaceLeft {
+ // If no room left, write the word on the next line.
+ buffer.WriteString("\n")
+ buffer.WriteString(delimiter)
+ buffer.WriteString(word)
+ spaceLeft = maxTextLen - wordLen
+ } else {
+ // Write word on this line
+ buffer.WriteByte(' ')
+ buffer.WriteString(word)
+ spaceLeft -= 1 + wordLen
+ }
+ }
+
+ return buffer.String()
+}
+
+// Configures the Output and log output io.Writers. Called before running
+// commands but after processing flags.
+func setupOutput() {
+ if VerboseFlag.Value {
+ log.SetOutput(os.Stdout)
+ } else {
+ log.SetOutput(ioutil.Discard)
+ }
+ if Output != nil {
+ return
+ }
+ if QuietFlag.Value {
+ Output = ioutil.Discard
+ } else {
+ Output = os.Stdout
+ }
+}
+
+// AskQuestion asks the user a yes or no question. Returning a boolean on a
+// successful answer and an error if there was not a response from the user.
+// Returns the defaultChoice on empty input (or in quiet mode).
+func AskQuestion(question string, defaultChoice bool) (bool, error) {
+ // If in quiet mode, we just use the default.
+ if QuietFlag.Value {
+ return defaultChoice, nil
+ }
+ // Loop until failure or valid input.
+ for {
+ if defaultChoice {
+ fmt.Fprintf(Output, "%s %s ", question, defaultYesSuffix)
+ } else {
+ fmt.Fprintf(Output, "%s %s ", question, defaultNoSuffix)
+ }
+
+ input, err := util.ReadLine()
+ if err != nil {
+ return false, err
+ }
+
+ switch strings.ToLower(input) {
+ case "y", "yes":
+ return true, nil
+ case "n", "no":
+ return false, nil
+ case "":
+ return defaultChoice, nil
+ }
+ }
+}
+
+// AskConfirmation asks the user for confirmation before performing a specific
+// action. An error is returned if the user declines or IO fails.
+func AskConfirmation(question, warning string, defaultChoice bool) error {
+ // All confirmations are "yes" if we are forcing.
+ if ForceFlag.Value {
+ return nil
+ }
+
+ // Defaults of "no" require forcing.
+ if QuietFlag.Value {
+ if defaultChoice {
+ return nil
+ }
+ return ErrMustForce
+ }
+
+ if warning != "" {
+ fmt.Fprintln(Output, wrapText("WARNING: "+warning, 0))
+ }
+
+ confirmed, err := AskQuestion(question, defaultChoice)
+ if err != nil {
+ return err
+ }
+ if !confirmed {
+ return ErrCanceled
+ }
+ return nil
+}
diff --git a/cmd/strings.go b/cmd/strings.go
new file mode 100644
index 0000000..559c60c
--- /dev/null
+++ b/cmd/strings.go
@@ -0,0 +1,34 @@
+/*
+ * strings.go - Strings and templates for output formatting
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+import (
+ "io"
+ "text/template"
+)
+
+// ExecuteTemplate creates an anonymous template the text, and runs it with the
+// provided writer and data. Panics if text has bad format or execution fails.
+func ExecuteTemplate(w io.Writer, text string, data interface{}) {
+ tmpl := template.Must(template.New("").Parse(text))
+ if err := tmpl.Execute(w, data); err != nil {
+ panic(err)
+ }
+}
diff --git a/cmd/templates.go b/cmd/templates.go
deleted file mode 100644
index e69de29..0000000
--- a/cmd/templates.go
+++ /dev/null
diff --git a/cmd/version.go b/cmd/version.go
new file mode 100644
index 0000000..787e2cd
--- /dev/null
+++ b/cmd/version.go
@@ -0,0 +1,62 @@
+/*
+ * version.go - Version subcommand that can be added to any binary
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 cmd
+
+// Templates for use with the version command, which both parse the Info var.
+var (
+ VersionShortTemplate = "{{.Command}} version {{.VersionTag}}\n"
+ VersionLongTemplate = VersionShortTemplate + `{{if .Compiled}}
+Compiled:
+ {{.Compiled}}
+{{end}}{{if len .Authors}}
+Author{{with $length := len .Authors}}{{if ne 1 $length}}s{{end}}{{end}}:{{range .Authors}}
+ {{.}}{{end}}
+{{end}}{{if .Copyright}}
+Copyright:
+ {{.Copyright}}
+{{end}}`
+)
+
+// Version is a command which will display either the VersionTag (by default) or
+// the full version information (version, copyright, authors).
+var Version = &Command{
+ Name: "version",
+ UsageLines: []string{""},
+ Flags: []Flag{longFlag},
+ Action: versionAction,
+}
+
+// Using longFlag with the version command displays the longer version info.
+var longFlag = &BoolFlag{
+ Name: "long",
+ Usage: "Print the detailed version and copyright information.",
+}
+
+func versionAction(_ []string) error {
+ if Info.VersionTag == "" {
+ return ErrUnknownVersion
+ }
+ if longFlag.Value {
+
+ } else {
+
+ }
+ return nil
+}
diff --git a/ext4/ext4.go b/ext4/ext4.go
index 7419929..063d68c 100644
--- a/ext4/ext4.go
+++ b/ext4/ext4.go
@@ -25,6 +25,9 @@ import (
"io"
"io/ioutil"
"os"
+ "time"
+
+ "github.com/urfave/cli"
)
var (
@@ -77,6 +80,38 @@ func printAndExit(err error, printUsage bool) {
}
func main() {
+ // Create our command line application
+ app := cli.NewApp()
+ app.Usage = shortUsage
+ app.Authors = Authors
+ app.Copyright = apache2GoogleCopyright
+
+ // Grab the version and compilation time passed in from the Makefile.
+ app.Version = version
+ app.Compiled, _ = time.Parse(time.UnixDate, buildTime)
+ app.OnUsageError = onUsageError
+
+ // Setup global flags
+ cli.HelpFlag = helpFlag
+ cli.VersionFlag = versionFlag
+ cli.VersionPrinter = func(c *cli.Context) {
+ cli.HelpPrinter(c.App.Writer, versionInfoTemplate, c.App)
+ }
+ app.Flags = universalFlags
+
+ // We hide the help subcommand so that "fscrypt <command> --help" works
+ // and "fscrypt <command> help" does not.
+ app.HideHelp = true
+
+ // Initialize command list and setup all of the commands.
+ app.Action = defaultAction
+ app.Commands = []cli.Command{Setup, Encrypt, Unlock, Purge, Status, Metadata}
+ for i := range app.Commands {
+ setupCommand(&app.Commands[i])
+ }
+
+ app.Run(os.Args)
+
set.SetOutput(ioutil.Discard)
if err := set.Parse(os.Args[1:]); err != nil {
printAndExit(err, true)
diff --git a/util/users.go b/util/users.go
new file mode 100644
index 0000000..92affa8
--- /dev/null
+++ b/util/users.go
@@ -0,0 +1,50 @@
+/*
+ * util.go - Functions for dealing with users
+ *
+ * Copyright 2017 Google Inc.
+ * Author: Joe Richey (joerichey@google.com)
+ *
+ * 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 util
+
+import (
+ "fmt"
+ "os"
+ "os/user"
+ "strconv"
+)
+
+// CurrentUserID returns the uid of the effective user.
+func CurrentUserID() int {
+ return os.Geteuid()
+}
+
+// GetUser returns the user entry corresponding to the provided uid.
+func GetUser(uid int) *user.User {
+ uidString := strconv.Itoa(uid)
+ foundUser, err := user.LookupId(uidString)
+ if err != nil {
+ return &user.User{
+ Uid: uidString,
+ Username: fmt.Sprintf("[uid=%d]", uid),
+ }
+ }
+ return foundUser
+}
+
+// CurrentUser returns the user entry for the effective user.
+func CurrentUser() *user.User {
+ return GetUser(CurrentUserID())
+}
diff --git a/util/util.go b/util/util.go
index 3de4a1a..9f62b45 100644
--- a/util/util.go
+++ b/util/util.go
@@ -27,7 +27,6 @@ import (
"bufio"
"math"
"os"
- "os/user"
"strconv"
"unsafe"
)
@@ -117,13 +116,3 @@ func AtoiOrPanic(input string) int {
}
return i
}
-
-// EffectiveUser returns the user entry corresponding to the effective user.
-func EffectiveUser() (*user.User, error) {
- return user.LookupId(strconv.Itoa(os.Geteuid()))
-}
-
-// IsUserRoot checks if the effective user is root.
-func IsUserRoot() bool {
- return os.Geteuid() == 0
-}