2016-09-20 19:45:28 +02:00
|
|
|
package main
|
|
|
|
|
2019-01-05 15:44:32 +01:00
|
|
|
// Should be initialized before anything else.
|
|
|
|
// This import line MUST be in the alphabitcally first source code file of
|
|
|
|
// package main!
|
|
|
|
import _ "github.com/rfjakob/gocryptfs/internal/ensurefds012"
|
|
|
|
|
2016-09-20 19:45:28 +02:00
|
|
|
import (
|
|
|
|
"flag"
|
2016-10-09 20:55:33 +02:00
|
|
|
"fmt"
|
2016-12-10 14:54:06 +01:00
|
|
|
"net"
|
2016-09-20 19:45:28 +02:00
|
|
|
"os"
|
|
|
|
"strconv"
|
2016-10-09 19:38:49 +02:00
|
|
|
"strings"
|
2018-10-06 21:49:33 +02:00
|
|
|
"time"
|
2016-09-20 19:45:28 +02:00
|
|
|
|
2017-06-05 22:45:11 +02:00
|
|
|
"github.com/hanwen/go-fuse/fuse"
|
2016-09-20 19:45:28 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
2017-05-07 22:15:01 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/exitcodes"
|
2016-09-20 19:45:28 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/prefer_openssl"
|
2018-08-15 23:31:37 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/readpassword"
|
2017-04-08 02:09:28 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
2016-09-20 19:45:28 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
|
|
|
)
|
|
|
|
|
2016-09-20 19:59:08 +02:00
|
|
|
// argContainer stores the parsed CLI options and arguments
|
2016-09-20 19:45:28 +02:00
|
|
|
type argContainer struct {
|
2016-11-01 18:59:34 +01:00
|
|
|
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
|
2016-09-20 19:45:28 +02:00
|
|
|
plaintextnames, quiet, nosyslog, wpanic,
|
2018-06-07 22:50:30 +02:00
|
|
|
longnames, allow_other, reverse, aessiv, nonempty, raw64,
|
2017-11-12 20:06:13 +01:00
|
|
|
noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
|
2018-06-17 15:25:09 +02:00
|
|
|
sharedstorage, devrandom, fsck, trezor bool
|
2018-06-07 22:50:30 +02:00
|
|
|
// Mount options with opposites
|
|
|
|
dev, nodev, suid, nosuid, exec, noexec, rw, ro bool
|
2016-09-20 19:59:08 +02:00
|
|
|
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
2017-06-05 22:45:11 +02:00
|
|
|
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
|
2018-08-11 23:26:49 +02:00
|
|
|
// For reverse mode, --exclude is available. It can be specified multiple times.
|
|
|
|
exclude multipleStrings
|
2016-09-20 19:59:08 +02:00
|
|
|
// Configuration file name override
|
|
|
|
config string
|
2016-09-20 19:45:28 +02:00
|
|
|
notifypid, scryptn int
|
2018-10-06 21:49:33 +02:00
|
|
|
// Idle time before autounmount
|
|
|
|
idle time.Duration
|
2016-12-10 14:54:06 +01:00
|
|
|
// Helper variables that are NOT cli options all start with an underscore
|
2016-10-08 20:57:38 +02:00
|
|
|
// _configCustom is true when the user sets a custom config file name.
|
|
|
|
_configCustom bool
|
2016-12-10 14:54:06 +01:00
|
|
|
// _ctlsockFd stores the control socket file descriptor (ctlsock stores the path)
|
|
|
|
_ctlsockFd net.Listener
|
2017-05-30 23:01:06 +02:00
|
|
|
// _forceOwner is, if non-nil, a parsed, validated Owner (as opposed to the string above)
|
2017-06-05 22:45:11 +02:00
|
|
|
_forceOwner *fuse.Owner
|
2016-09-20 19:45:28 +02:00
|
|
|
}
|
|
|
|
|
2018-08-11 23:25:17 +02:00
|
|
|
type multipleStrings []string
|
|
|
|
|
|
|
|
func (s *multipleStrings) String() string {
|
|
|
|
s2 := []string(*s)
|
|
|
|
return fmt.Sprint(s2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *multipleStrings) Set(val string) error {
|
|
|
|
*s = append(*s, val)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-09-20 19:45:28 +02:00
|
|
|
var flagSet *flag.FlagSet
|
|
|
|
|
2016-10-09 19:38:49 +02:00
|
|
|
// prefixOArgs transform options passed via "-o foo,bar" into regular options
|
|
|
|
// like "-foo -bar" and prefixes them to the command line.
|
2017-04-29 15:11:17 +02:00
|
|
|
// Testcases in TestPrefixOArgs().
|
2018-06-05 21:02:35 +02:00
|
|
|
func prefixOArgs(osArgs []string) ([]string, error) {
|
2017-04-29 15:11:17 +02:00
|
|
|
// Need at least 3, example: gocryptfs -o foo,bar
|
|
|
|
// ^ 0 ^ 1 ^ 2
|
2016-10-10 19:44:34 +02:00
|
|
|
if len(osArgs) < 3 {
|
2018-06-05 21:02:35 +02:00
|
|
|
return osArgs, nil
|
2016-10-09 19:38:49 +02:00
|
|
|
}
|
2017-04-29 15:11:17 +02:00
|
|
|
// Passing "--" disables "-o" parsing. Ignore element 0 (program name).
|
|
|
|
for _, v := range osArgs[1:] {
|
|
|
|
if v == "--" {
|
2018-06-05 21:02:35 +02:00
|
|
|
return osArgs, nil
|
2017-04-29 15:11:17 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 19:44:34 +02:00
|
|
|
// Find and extract "-o foo,bar"
|
|
|
|
var otherArgs, oOpts []string
|
|
|
|
for i := 1; i < len(osArgs); i++ {
|
|
|
|
if osArgs[i] == "-o" {
|
|
|
|
// Last argument?
|
|
|
|
if i+1 >= len(osArgs) {
|
2018-06-05 21:02:35 +02:00
|
|
|
return nil, fmt.Errorf("The \"-o\" option requires an argument")
|
2016-10-10 19:44:34 +02:00
|
|
|
}
|
|
|
|
oOpts = strings.Split(osArgs[i+1], ",")
|
|
|
|
// Skip over the arguments to "-o"
|
|
|
|
i++
|
|
|
|
} else if strings.HasPrefix(osArgs[i], "-o=") {
|
|
|
|
oOpts = strings.Split(osArgs[i][3:], ",")
|
|
|
|
} else {
|
|
|
|
otherArgs = append(otherArgs, osArgs[i])
|
|
|
|
}
|
2016-10-09 19:38:49 +02:00
|
|
|
}
|
2016-10-10 19:44:34 +02:00
|
|
|
// Start with program name
|
2016-10-09 19:38:49 +02:00
|
|
|
newArgs := []string{osArgs[0]}
|
|
|
|
// Add options from "-o"
|
2016-10-10 19:44:34 +02:00
|
|
|
for _, o := range oOpts {
|
|
|
|
if o == "" {
|
|
|
|
continue
|
|
|
|
}
|
2016-10-10 20:21:52 +02:00
|
|
|
if o == "o" || o == "-o" {
|
|
|
|
tlog.Fatal.Printf("You can't pass \"-o\" to \"-o\"")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2016-10-10 20:21:52 +02:00
|
|
|
}
|
2016-10-10 19:44:34 +02:00
|
|
|
newArgs = append(newArgs, "-"+o)
|
2016-10-09 19:38:49 +02:00
|
|
|
}
|
2016-10-10 19:44:34 +02:00
|
|
|
// Add other arguments
|
|
|
|
newArgs = append(newArgs, otherArgs...)
|
2018-06-05 21:02:35 +02:00
|
|
|
return newArgs, nil
|
2016-10-09 19:38:49 +02:00
|
|
|
}
|
|
|
|
|
2016-09-20 19:59:08 +02:00
|
|
|
// parseCliOpts - parse command line options (i.e. arguments that start with "-")
|
|
|
|
func parseCliOpts() (args argContainer) {
|
2016-09-20 19:45:28 +02:00
|
|
|
var err error
|
|
|
|
var opensslAuto string
|
|
|
|
|
2018-06-05 21:02:35 +02:00
|
|
|
os.Args, err = prefixOArgs(os.Args)
|
|
|
|
if err != nil {
|
|
|
|
tlog.Fatal.Println(err)
|
|
|
|
os.Exit(exitcodes.Usage)
|
|
|
|
}
|
|
|
|
|
2016-10-09 20:55:33 +02:00
|
|
|
flagSet = flag.NewFlagSet(tlog.ProgramName, flag.ContinueOnError)
|
2018-06-08 00:03:23 +02:00
|
|
|
flagSet.Usage = func() {}
|
2016-09-20 19:45:28 +02:00
|
|
|
flagSet.BoolVar(&args.debug, "d", false, "")
|
|
|
|
flagSet.BoolVar(&args.debug, "debug", false, "Enable debug output")
|
|
|
|
flagSet.BoolVar(&args.fusedebug, "fusedebug", false, "Enable fuse library debug output")
|
|
|
|
flagSet.BoolVar(&args.init, "init", false, "Initialize encrypted directory")
|
|
|
|
flagSet.BoolVar(&args.zerokey, "zerokey", false, "Use all-zero dummy master key")
|
|
|
|
// Tri-state true/false/auto
|
|
|
|
flagSet.StringVar(&opensslAuto, "openssl", "auto", "Use OpenSSL instead of built-in Go crypto")
|
|
|
|
flagSet.BoolVar(&args.passwd, "passwd", false, "Change password")
|
2016-11-01 18:59:34 +01:00
|
|
|
flagSet.BoolVar(&args.fg, "f", false, "")
|
|
|
|
flagSet.BoolVar(&args.fg, "fg", false, "Stay in the foreground")
|
2016-09-20 19:45:28 +02:00
|
|
|
flagSet.BoolVar(&args.version, "version", false, "Print version and exit")
|
|
|
|
flagSet.BoolVar(&args.plaintextnames, "plaintextnames", false, "Do not encrypt file names")
|
|
|
|
flagSet.BoolVar(&args.quiet, "q", false, "")
|
|
|
|
flagSet.BoolVar(&args.quiet, "quiet", false, "Quiet - silence informational messages")
|
|
|
|
flagSet.BoolVar(&args.nosyslog, "nosyslog", false, "Do not redirect output to syslog when running in the background")
|
|
|
|
flagSet.BoolVar(&args.wpanic, "wpanic", false, "When encountering a warning, panic and exit immediately")
|
|
|
|
flagSet.BoolVar(&args.longnames, "longnames", true, "Store names longer than 176 bytes in extra files")
|
|
|
|
flagSet.BoolVar(&args.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+
|
|
|
|
"Only works if user_allow_other is set in /etc/fuse.conf.")
|
|
|
|
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
|
2016-09-26 23:25:13 +02:00
|
|
|
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
|
2016-10-06 22:41:13 +02:00
|
|
|
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
|
2017-03-06 22:59:30 +01:00
|
|
|
flagSet.BoolVar(&args.raw64, "raw64", true, "Use unpadded base64 for file names")
|
2016-11-24 22:36:04 +01:00
|
|
|
flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing")
|
2017-02-22 23:55:43 +01:00
|
|
|
flagSet.BoolVar(&args.speed, "speed", false, "Run crypto speed test")
|
2017-03-06 22:43:55 +01:00
|
|
|
flagSet.BoolVar(&args.hkdf, "hkdf", true, "Use HKDF as an additional key derivation step")
|
2017-03-18 16:01:50 +01:00
|
|
|
flagSet.BoolVar(&args.serialize_reads, "serialize_reads", false, "Try to serialize read operations")
|
2017-04-08 02:09:28 +02:00
|
|
|
flagSet.BoolVar(&args.forcedecode, "forcedecode", false, "Force decode of files even if integrity check fails."+
|
|
|
|
" Requires gocryptfs to be compiled with openssl support and implies -openssl true")
|
2017-05-30 17:59:13 +02:00
|
|
|
flagSet.BoolVar(&args.hh, "hh", false, "Show this long help text")
|
2017-05-30 19:01:32 +02:00
|
|
|
flagSet.BoolVar(&args.info, "info", false, "Display information about CIPHERDIR")
|
2017-11-12 20:06:13 +01:00
|
|
|
flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
|
2017-11-19 13:30:04 +01:00
|
|
|
flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key")
|
2018-04-01 14:25:10 +02:00
|
|
|
flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR")
|
2018-08-15 23:31:37 +02:00
|
|
|
if readpassword.TrezorSupport {
|
|
|
|
flagSet.BoolVar(&args.trezor, "trezor", false, "Protect the masterkey using a SatoshiLabs Trezor instead of a password")
|
|
|
|
}
|
2018-06-07 22:50:30 +02:00
|
|
|
|
|
|
|
// Mount options with opposites
|
|
|
|
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")
|
|
|
|
flagSet.BoolVar(&args.nodev, "nodev", false, "Deny device files")
|
|
|
|
flagSet.BoolVar(&args.suid, "suid", false, "Allow suid binaries")
|
|
|
|
flagSet.BoolVar(&args.nosuid, "nosuid", false, "Deny suid binaries")
|
|
|
|
flagSet.BoolVar(&args.exec, "exec", false, "Allow executables")
|
|
|
|
flagSet.BoolVar(&args.noexec, "noexec", false, "Deny executables")
|
|
|
|
flagSet.BoolVar(&args.rw, "rw", false, "Mount the filesystem read-write")
|
|
|
|
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
|
|
|
|
|
2016-09-20 19:45:28 +02:00
|
|
|
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
|
|
|
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
|
|
|
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
|
|
|
|
flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf")
|
|
|
|
flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt")
|
2016-10-09 20:08:10 +02:00
|
|
|
flagSet.StringVar(&args.passfile, "passfile", "", "Read password from file")
|
2016-10-09 19:32:55 +02:00
|
|
|
flagSet.StringVar(&args.ko, "ko", "", "Pass additional options directly to the kernel, comma-separated list")
|
2016-11-10 00:27:08 +01:00
|
|
|
flagSet.StringVar(&args.ctlsock, "ctlsock", "", "Create control socket at specified path")
|
2017-01-26 22:13:57 +01:00
|
|
|
flagSet.StringVar(&args.fsname, "fsname", "", "Override the filesystem name")
|
2017-05-30 23:01:06 +02:00
|
|
|
flagSet.StringVar(&args.force_owner, "force_owner", "", "uid:gid pair to coerce ownership")
|
2017-06-05 22:45:11 +02:00
|
|
|
flagSet.StringVar(&args.trace, "trace", "", "Write execution trace to file")
|
2018-08-11 23:26:49 +02:00
|
|
|
|
2018-08-15 13:11:34 +02:00
|
|
|
// -e, --exclude
|
|
|
|
flagSet.Var(&args.exclude, "e", "Alias for -exclude")
|
2018-08-11 23:26:49 +02:00
|
|
|
flagSet.Var(&args.exclude, "exclude", "Exclude relative path from reverse view")
|
|
|
|
|
2016-09-20 19:45:28 +02:00
|
|
|
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
|
|
|
|
"successful mount - used internally for daemonization")
|
2017-03-25 18:22:08 +01:00
|
|
|
flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+
|
|
|
|
"A lower value speeds up mounting and reduces its memory needs, but makes the password susceptible to brute-force attacks")
|
2018-10-06 21:49:33 +02:00
|
|
|
|
|
|
|
flagSet.DurationVar(&args.idle, "i", 0, "Alias for -idle")
|
|
|
|
flagSet.DurationVar(&args.idle, "idle", 0, "Auto-unmount after specified idle duration (ignored in reverse mode). "+
|
|
|
|
"Durations are specified like \"500s\" or \"2h45m\". 0 means stay mounted indefinitely.")
|
|
|
|
|
2016-10-10 20:21:52 +02:00
|
|
|
var dummyString string
|
2017-05-30 17:59:13 +02:00
|
|
|
flagSet.StringVar(&dummyString, "o", "", "For compatibility with mount(1), options can be also passed as a comma-separated list to -o on the end.")
|
2016-10-09 20:06:23 +02:00
|
|
|
// Actual parsing
|
2016-10-09 20:55:33 +02:00
|
|
|
err = flagSet.Parse(os.Args[1:])
|
2016-10-10 20:21:52 +02:00
|
|
|
if err == flag.ErrHelp {
|
2018-06-08 00:03:23 +02:00
|
|
|
helpShort()
|
2016-10-10 20:21:52 +02:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
2016-10-09 20:55:33 +02:00
|
|
|
if err != nil {
|
2018-06-08 00:03:23 +02:00
|
|
|
tlog.Fatal.Printf("Invalid command line: %s. Try '%s -help'.", prettyArgs(), tlog.ProgramName)
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2016-10-09 20:55:33 +02:00
|
|
|
}
|
2016-09-20 19:45:28 +02:00
|
|
|
// "-openssl" needs some post-processing
|
|
|
|
if opensslAuto == "auto" {
|
|
|
|
args.openssl = prefer_openssl.PreferOpenSSL()
|
|
|
|
} else {
|
|
|
|
args.openssl, err = strconv.ParseBool(opensslAuto)
|
|
|
|
if err != nil {
|
|
|
|
tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err)
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2016-09-20 19:45:28 +02:00
|
|
|
}
|
|
|
|
}
|
2017-04-08 02:09:28 +02:00
|
|
|
// "-forcedecode" only works with openssl. Check compilation and command line parameters
|
|
|
|
if args.forcedecode == true {
|
|
|
|
if stupidgcm.BuiltWithoutOpenssl == true {
|
|
|
|
tlog.Fatal.Printf("The -forcedecode flag requires openssl support, but gocryptfs was compiled without it!")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2017-04-08 02:09:28 +02:00
|
|
|
}
|
|
|
|
if args.aessiv == true {
|
|
|
|
tlog.Fatal.Printf("The -forcedecode and -aessiv flags are incompatible because they use different crypto libs (openssl vs native Go)")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2017-04-08 02:09:28 +02:00
|
|
|
}
|
|
|
|
if args.reverse == true {
|
|
|
|
tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2017-04-08 02:09:28 +02:00
|
|
|
}
|
2017-08-21 21:06:05 +02:00
|
|
|
// Has the user explicitly disabled openssl using "-openssl=false/0"?
|
2017-04-24 00:25:02 +02:00
|
|
|
if !args.openssl && opensslAuto != "auto" {
|
|
|
|
tlog.Fatal.Printf("-forcedecode requires openssl, but is disabled via command-line option")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2017-04-08 02:09:28 +02:00
|
|
|
}
|
|
|
|
args.openssl = true
|
2017-04-24 00:25:02 +02:00
|
|
|
|
|
|
|
// Try to make it harder for the user to shoot himself in the foot.
|
|
|
|
args.ro = true
|
|
|
|
args.allow_other = false
|
|
|
|
args.ko = "noexec"
|
2017-04-08 02:09:28 +02:00
|
|
|
}
|
2018-12-15 17:09:38 +01:00
|
|
|
if args.extpass != "" && args.passfile != "" {
|
|
|
|
tlog.Fatal.Printf("The options -extpass and -passfile cannot be used at the same time")
|
|
|
|
os.Exit(exitcodes.Usage)
|
|
|
|
}
|
|
|
|
if args.passfile != "" && args.masterkey != "" {
|
|
|
|
tlog.Fatal.Printf("The options -passfile and -masterkey cannot be used at the same time")
|
|
|
|
os.Exit(exitcodes.Usage)
|
2016-10-09 20:08:10 +02:00
|
|
|
}
|
2016-10-16 16:50:23 +02:00
|
|
|
if args.extpass != "" && args.masterkey != "" {
|
|
|
|
tlog.Fatal.Printf("The options -extpass and -masterkey cannot be used at the same time")
|
2017-05-07 22:15:01 +02:00
|
|
|
os.Exit(exitcodes.Usage)
|
2016-10-16 16:50:23 +02:00
|
|
|
}
|
2018-06-25 21:55:20 +02:00
|
|
|
if args.extpass != "" && args.trezor {
|
|
|
|
tlog.Fatal.Printf("The options -extpass and -trezor cannot be used at the same time")
|
|
|
|
os.Exit(exitcodes.Usage)
|
|
|
|
}
|
2018-10-06 21:49:33 +02:00
|
|
|
if args.idle < 0 {
|
|
|
|
tlog.Fatal.Printf("Idle timeout cannot be less than 0")
|
|
|
|
os.Exit(exitcodes.Usage)
|
|
|
|
}
|
2016-09-20 19:45:28 +02:00
|
|
|
return args
|
|
|
|
}
|
2016-10-09 20:55:33 +02:00
|
|
|
|
|
|
|
// prettyArgs pretty-prints the command-line arguments.
|
|
|
|
func prettyArgs() string {
|
2018-06-08 00:03:23 +02:00
|
|
|
pa := fmt.Sprintf("%v", os.Args)
|
2016-10-09 20:55:33 +02:00
|
|
|
// Get rid of "[" and "]"
|
|
|
|
pa = pa[1 : len(pa)-1]
|
|
|
|
return pa
|
|
|
|
}
|
2018-04-01 14:25:10 +02:00
|
|
|
|
|
|
|
// countOpFlags counts the number of operation flags we were passed.
|
|
|
|
func countOpFlags(args *argContainer) int {
|
|
|
|
var count int
|
|
|
|
if args.info {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
if args.passwd {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
if args.init {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
if args.fsck {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|