lint fixes

This commit is contained in:
Valient Gough 2016-10-01 21:14:18 -07:00 committed by Jakob Unterwurzacher
parent 31a8f8b839
commit b764917cd5
34 changed files with 316 additions and 213 deletions

View File

@ -71,7 +71,7 @@ func parseCliOpts() (args argContainer) {
args.openssl, err = strconv.ParseBool(opensslAuto) args.openssl, err = strconv.ParseBool(opensslAuto)
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err) tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err)
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
} }

View File

@ -12,8 +12,8 @@ import (
) )
const ( const (
IVLen = contentenc.DefaultIVBits / 8 ivLen = contentenc.DefaultIVBits / 8
blockSize = contentenc.DefaultBS + IVLen + cryptocore.AuthTagLen blockSize = contentenc.DefaultBS + ivLen + cryptocore.AuthTagLen
) )
func errExit(err error) { func errExit(err error) {
@ -22,7 +22,7 @@ func errExit(err error) {
} }
func prettyPrintHeader(h *contentenc.FileHeader) { func prettyPrintHeader(h *contentenc.FileHeader) {
id := hex.EncodeToString(h.Id) id := hex.EncodeToString(h.ID)
fmt.Printf("Header: Version: %d, Id: %s\n", h.Version, id) fmt.Printf("Header: Version: %d, Id: %s\n", h.Version, id)
} }
@ -38,13 +38,13 @@ func main() {
errExit(err) errExit(err)
} }
headerBytes := make([]byte, contentenc.HEADER_LEN) headerBytes := make([]byte, contentenc.HeaderLen)
n, err := fd.ReadAt(headerBytes, 0) n, err := fd.ReadAt(headerBytes, 0)
if err == io.EOF && n == 0 { if err == io.EOF && n == 0 {
fmt.Println("empty file") fmt.Println("empty file")
os.Exit(0) os.Exit(0)
} else if err == io.EOF { } else if err == io.EOF {
fmt.Printf("incomplete file header: read %d bytes, want %d\n", n, contentenc.HEADER_LEN) fmt.Printf("incomplete file header: read %d bytes, want %d\n", n, contentenc.HeaderLen)
os.Exit(1) os.Exit(1)
} else if err != nil { } else if err != nil {
errExit(err) errExit(err)
@ -57,8 +57,8 @@ func main() {
var i int64 var i int64
for i = 0; ; i++ { for i = 0; ; i++ {
blockLen := int64(blockSize) blockLen := int64(blockSize)
off := contentenc.HEADER_LEN + i*blockSize off := contentenc.HeaderLen + i*blockSize
iv := make([]byte, IVLen) iv := make([]byte, ivLen)
_, err := fd.ReadAt(iv, off) _, err := fd.ReadAt(iv, off)
if err == io.EOF { if err == io.EOF {
break break
@ -76,7 +76,7 @@ func main() {
if err2 != nil { if err2 != nil {
errExit(err2) errExit(err2)
} }
blockLen = (fi.Size() - contentenc.HEADER_LEN) % blockSize blockLen = (fi.Size() - contentenc.HeaderLen) % blockSize
} else if err != nil { } else if err != nil {
errExit(err) errExit(err)
} }

View File

@ -22,13 +22,13 @@ func initDir(args *argContainer) {
_, err = os.Stat(args.config) _, err = os.Stat(args.config)
if err == nil { if err == nil {
tlog.Fatal.Printf("Config file %q already exists", args.config) tlog.Fatal.Printf("Config file %q already exists", args.config)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
} else { } else {
err = checkDirEmpty(args.cipherdir) err = checkDirEmpty(args.cipherdir)
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid cipherdir: %v", err) tlog.Fatal.Printf("Invalid cipherdir: %v", err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
} }
// Choose password for config file // Choose password for config file
@ -40,7 +40,7 @@ func initDir(args *argContainer) {
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv) err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
// Forward mode with filename encryption enabled needs a gocryptfs.diriv // Forward mode with filename encryption enabled needs a gocryptfs.diriv
// in the root dir // in the root dir
@ -48,7 +48,7 @@ func initDir(args *argContainer) {
err = nametransform.WriteDirIV(args.cipherdir) err = nametransform.WriteDirIV(args.cipherdir)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
} }
mountArgs := "" mountArgs := ""

View File

@ -14,31 +14,35 @@ import (
import "os" import "os"
const ( const (
// ConfDefaultName is the default configuration file name.
// The dot "." is not used in base64url (RFC4648), hence // The dot "." is not used in base64url (RFC4648), hence
// we can never clash with an encrypted file. // we can never clash with an encrypted file.
ConfDefaultName = "gocryptfs.conf" ConfDefaultName = "gocryptfs.conf"
// In reverse mode, the config file gets stored next to the plain-text // ConfReverseName is the default configuration file name in reverse mode,
// files. Make it hidden (start with dot) to not annoy the user. // the config file gets stored next to the plain-text files. Make it hidden
// (start with dot) to not annoy the user.
ConfReverseName = ".gocryptfs.reverse.conf" ConfReverseName = ".gocryptfs.reverse.conf"
) )
// ConfFile is the content of a config file.
type ConfFile struct { type ConfFile struct {
// gocryptfs version string // Creator is the gocryptfs version string.
// This only documents the config file for humans who look at it. The actual // This only documents the config file for humans who look at it. The actual
// technical info is contained in FeatureFlags. // technical info is contained in FeatureFlags.
Creator string Creator string
// Encrypted AES key, unlocked using a password hashed with scrypt // EncryptedKey holds an encrypted AES key, unlocked using a password
// hashed with scrypt
EncryptedKey []byte EncryptedKey []byte
// Stores parameters for scrypt hashing (key derivation) // ScryptObject stores parameters for scrypt hashing (key derivation)
ScryptObject scryptKdf ScryptObject ScryptKDF
// The On-Disk-Format version this filesystem uses // Version is the On-Disk-Format version this filesystem uses
Version uint16 Version uint16
// List of feature flags this filesystem has enabled. // FeatureFlags is a list of feature flags this filesystem has enabled.
// If gocryptfs encounters a feature flag it does not support, it will refuse // If gocryptfs encounters a feature flag it does not support, it will refuse
// mounting. This mechanism is analogous to the ext4 feature flags that are // mounting. This mechanism is analogous to the ext4 feature flags that are
// stored in the superblock. // stored in the superblock.
FeatureFlags []string FeatureFlags []string
// File the config is saved to. Not exported to JSON. // Filename is the name of the config file. Not exported to JSON.
filename string filename string
} }
@ -162,7 +166,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
// cf.ScryptObject. // cf.ScryptObject.
func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) { func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
// Generate derived key from password // Generate derived key from password
cf.ScryptObject = NewScryptKdf(logN) cf.ScryptObject = NewScryptKDF(logN)
scryptHash := cf.ScryptObject.DeriveKey(password) scryptHash := cf.ScryptObject.DeriveKey(password)
// Lock master key using password-based key // Lock master key using password-based key

View File

@ -3,16 +3,24 @@ package configfile
type flagIota int type flagIota int
const ( const (
// FlagPlaintextNames indicates that filenames are unencrypted.
FlagPlaintextNames flagIota = iota FlagPlaintextNames flagIota = iota
// FlagDirIV indicates that a per-directory IV file is used.
FlagDirIV FlagDirIV
// FlagEMENames indicates EME (ECB-Mix-ECB) filename encryption.
// This flag is mandatory since gocryptfs v1.0.
FlagEMENames FlagEMENames
// FlagGCMIV128 indicates 128-bit GCM IVs.
// This flag is mandatory since gocryptfs v1.0.
FlagGCMIV128 FlagGCMIV128
// FlagLongNames allows file names longer than 176 bytes.
FlagLongNames FlagLongNames
// FlagAESSIV selects an AES-SIV based crypto backend.
FlagAESSIV FlagAESSIV
) )
// knownFlags stores the known feature flags and their string representation // knownFlags stores the known feature flags and their string representation
var knownFlags map[flagIota]string = map[flagIota]string{ var knownFlags = map[flagIota]string{
FlagPlaintextNames: "PlaintextNames", FlagPlaintextNames: "PlaintextNames",
FlagDirIV: "DirIV", FlagDirIV: "DirIV",
FlagEMENames: "EMENames", FlagEMENames: "EMENames",
@ -22,7 +30,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{
} }
// Filesystems that do not have these feature flags set are deprecated. // Filesystems that do not have these feature flags set are deprecated.
var requiredFlagsNormal []flagIota = []flagIota{ var requiredFlagsNormal = []flagIota{
FlagDirIV, FlagDirIV,
FlagEMENames, FlagEMENames,
FlagGCMIV128, FlagGCMIV128,
@ -30,11 +38,11 @@ var requiredFlagsNormal []flagIota = []flagIota{
// Filesystems without filename encryption obviously don't have or need the // Filesystems without filename encryption obviously don't have or need the
// filename related feature flags. // filename related feature flags.
var requiredFlagsPlaintextNames []flagIota = []flagIota{ var requiredFlagsPlaintextNames = []flagIota{
FlagGCMIV128, FlagGCMIV128,
} }
// isFeatureFlagKnown verifies that we understand a feature flag // isFeatureFlagKnown verifies that we understand a feature flag.
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool { func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
for _, knownFlag := range knownFlags { for _, knownFlag := range knownFlags {
if knownFlag == flag { if knownFlag == flag {
@ -44,7 +52,7 @@ func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
return false return false
} }
// isFeatureFlagSet - is the feature flag "flagWant" enabled? // IsFeatureFlagSet returns true if the feature flag "flagWant" is enabled.
func (cf *ConfFile) IsFeatureFlagSet(flagWant flagIota) bool { func (cf *ConfFile) IsFeatureFlagSet(flagWant flagIota) bool {
flagString := knownFlags[flagWant] flagString := knownFlags[flagWant]
for _, flag := range cf.FeatureFlags { for _, flag := range cf.FeatureFlags {

View File

@ -12,12 +12,14 @@ import (
) )
const ( const (
// ScryptDefaultLogN is the default scrypt logN configuration parameter.
// 1 << 16 uses 64MB of memory, // 1 << 16 uses 64MB of memory,
// takes 4 seconds on my Atom Z3735F netbook // takes 4 seconds on my Atom Z3735F netbook
ScryptDefaultLogN = 16 ScryptDefaultLogN = 16
) )
type scryptKdf struct { // ScryptKDF is an instance of the scrypt key deriviation function.
type ScryptKDF struct {
Salt []byte Salt []byte
N int N int
R int R int
@ -25,8 +27,9 @@ type scryptKdf struct {
KeyLen int KeyLen int
} }
func NewScryptKdf(logN int) scryptKdf { // NewScryptKDF returns a new instance of ScryptKDF.
var s scryptKdf func NewScryptKDF(logN int) ScryptKDF {
var s ScryptKDF
s.Salt = cryptocore.RandBytes(cryptocore.KeyLen) s.Salt = cryptocore.RandBytes(cryptocore.KeyLen)
if logN <= 0 { if logN <= 0 {
s.N = 1 << ScryptDefaultLogN s.N = 1 << ScryptDefaultLogN
@ -43,7 +46,8 @@ func NewScryptKdf(logN int) scryptKdf {
return s return s
} }
func (s *scryptKdf) DeriveKey(pw string) []byte { // DeriveKey returns a new key from a supplied password.
func (s *ScryptKDF) DeriveKey(pw string) []byte {
k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen) k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen)
if err != nil { if err != nil {
log.Panicf("DeriveKey failed: %v", err) log.Panicf("DeriveKey failed: %v", err)
@ -53,6 +57,6 @@ func (s *scryptKdf) DeriveKey(pw string) []byte {
// LogN - N is saved as 2^LogN, but LogN is much easier to work with. // LogN - N is saved as 2^LogN, but LogN is much easier to work with.
// This function gives you LogN = Log2(N). // This function gives you LogN = Log2(N).
func (s *scryptKdf) LogN() int { func (s *ScryptKDF) LogN() int {
return int(math.Log2(float64(s.N)) + 0.5) return int(math.Log2(float64(s.N)) + 0.5)
} }

View File

@ -21,7 +21,7 @@ ok github.com/rfjakob/gocryptfs/cryptfs 18.772s
*/ */
func benchmarkScryptN(n int, b *testing.B) { func benchmarkScryptN(n int, b *testing.B) {
kdf := NewScryptKdf(n) kdf := NewScryptKDF(n)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
kdf.DeriveKey("test") kdf.DeriveKey("test")
} }

View File

@ -11,21 +11,28 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// NonceMode determines how nonces are created.
type NonceMode int type NonceMode int
const ( const (
// Default plaintext block size // DefaultBS is the default plaintext block size
DefaultBS = 4096 DefaultBS = 4096
// DefaultIVBits is the default length of IV, in bits.
// We always use 128-bit IVs for file content, but the // We always use 128-bit IVs for file content, but the
// key in the config file is encrypted with a 96-bit IV. // key in the config file is encrypted with a 96-bit IV.
DefaultIVBits = 128 DefaultIVBits = 128
_ = iota // skip zero _ = iota // skip zero
RandomNonce NonceMode = iota // RandomNonce chooses a random nonce.
RandomNonce NonceMode = iota
// ReverseDeterministicNonce chooses a deterministic nonce, suitable for
// use in reverse mode.
ReverseDeterministicNonce NonceMode = iota ReverseDeterministicNonce NonceMode = iota
ExternalNonce NonceMode = iota // ExternalNonce derives a nonce from external sources.
ExternalNonce NonceMode = iota
) )
// ContentEnc is used to encipher and decipher file content.
type ContentEnc struct { type ContentEnc struct {
// Cryptographic primitives // Cryptographic primitives
cryptoCore *cryptocore.CryptoCore cryptoCore *cryptocore.CryptoCore
@ -39,8 +46,8 @@ type ContentEnc struct {
allZeroNonce []byte allZeroNonce []byte
} }
// New returns an initialized ContentEnc instance.
func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc {
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
return &ContentEnc{ return &ContentEnc{
@ -62,16 +69,16 @@ func (be *ContentEnc) CipherBS() uint64 {
return be.cipherBS return be.cipherBS
} }
// DecryptBlocks - Decrypt a number of blocks // DecryptBlocks decrypts a number of blocks
// TODO refactor to three-param for // TODO refactor to three-param for
func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) { func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileID []byte) ([]byte, error) {
cBuf := bytes.NewBuffer(ciphertext) cBuf := bytes.NewBuffer(ciphertext)
var err error var err error
var pBuf bytes.Buffer var pBuf bytes.Buffer
for cBuf.Len() > 0 { for cBuf.Len() > 0 {
cBlock := cBuf.Next(int(be.cipherBS)) cBlock := cBuf.Next(int(be.cipherBS))
var pBlock []byte var pBlock []byte
pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileId) pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID)
if err != nil { if err != nil {
break break
} }
@ -85,7 +92,7 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file
// //
// Corner case: A full-sized block of all-zero ciphertext bytes is translated // Corner case: A full-sized block of all-zero ciphertext bytes is translated
// to an all-zero plaintext block, i.e. file hole passtrough. // to an all-zero plaintext block, i.e. file hole passtrough.
func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte) ([]byte, error) { func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []byte) ([]byte, error) {
// Empty block? // Empty block?
if len(ciphertext) == 0 { if len(ciphertext) == 0 {
@ -114,7 +121,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
// Decrypt // Decrypt
var plaintext []byte var plaintext []byte
aData := make([]byte, 8) aData := make([]byte, 8)
aData = append(aData, fileId...) aData = append(aData, fileID...)
binary.BigEndian.PutUint64(aData, blockNo) binary.BigEndian.PutUint64(aData, blockNo)
plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData) plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData)

View File

@ -63,7 +63,7 @@ func TestCiphertextRange(t *testing.T) {
if alignedLength < r.length { if alignedLength < r.length {
t.Errorf("alignedLength=%d is smaller than length=%d", alignedLength, r.length) t.Errorf("alignedLength=%d is smaller than length=%d", alignedLength, r.length)
} }
if (alignedOffset-HEADER_LEN)%f.cipherBS != 0 { if (alignedOffset-HeaderLen)%f.cipherBS != 0 {
t.Errorf("alignedOffset=%d is not aligned", alignedOffset) t.Errorf("alignedOffset=%d is not aligned", alignedOffset)
} }
if r.offset%f.plainBS != 0 && skipBytes == 0 { if r.offset%f.plainBS != 0 && skipBytes == 0 {
@ -81,7 +81,7 @@ func TestBlockNo(t *testing.T) {
if b != 0 { if b != 0 {
t.Errorf("actual: %d", b) t.Errorf("actual: %d", b)
} }
b = f.CipherOffToBlockNo(HEADER_LEN + f.cipherBS) b = f.CipherOffToBlockNo(HeaderLen + f.cipherBS)
if b != 1 { if b != 1 {
t.Errorf("actual: %d", b) t.Errorf("actual: %d", b)
} }

View File

@ -12,42 +12,44 @@ import (
) )
const ( const (
// Current On-Disk-Format version // CurrentVersion is the current On-Disk-Format version
CurrentVersion = 2 CurrentVersion = 2
HEADER_VERSION_LEN = 2 // uint16 headerVersionLen = 2 // uint16
HEADER_ID_LEN = 16 // 128 bit random file id headerIDLen = 16 // 128 bit random file id
HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length // HeaderLen is the total header length
HeaderLen = headerVersionLen + headerIDLen
) )
// FileHeader represents the header stored on each non-empty file.
type FileHeader struct { type FileHeader struct {
Version uint16 Version uint16
Id []byte ID []byte
} }
// Pack - serialize fileHeader object // Pack - serialize fileHeader object
func (h *FileHeader) Pack() []byte { func (h *FileHeader) Pack() []byte {
if len(h.Id) != HEADER_ID_LEN || h.Version != CurrentVersion { if len(h.ID) != headerIDLen || h.Version != CurrentVersion {
panic("FileHeader object not properly initialized") panic("FileHeader object not properly initialized")
} }
buf := make([]byte, HEADER_LEN) buf := make([]byte, HeaderLen)
binary.BigEndian.PutUint16(buf[0:HEADER_VERSION_LEN], h.Version) binary.BigEndian.PutUint16(buf[0:headerVersionLen], h.Version)
copy(buf[HEADER_VERSION_LEN:], h.Id) copy(buf[headerVersionLen:], h.ID)
return buf return buf
} }
// ParseHeader - parse "buf" into fileHeader object // ParseHeader - parse "buf" into fileHeader object
func ParseHeader(buf []byte) (*FileHeader, error) { func ParseHeader(buf []byte) (*FileHeader, error) {
if len(buf) != HEADER_LEN { if len(buf) != HeaderLen {
return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HEADER_LEN) return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HeaderLen)
} }
var h FileHeader var h FileHeader
h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN]) h.Version = binary.BigEndian.Uint16(buf[0:headerVersionLen])
if h.Version != CurrentVersion { if h.Version != CurrentVersion {
return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, CurrentVersion) return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, CurrentVersion)
} }
h.Id = buf[HEADER_VERSION_LEN:] h.ID = buf[headerVersionLen:]
return &h, nil return &h, nil
} }
@ -55,6 +57,6 @@ func ParseHeader(buf []byte) (*FileHeader, error) {
func RandomHeader() *FileHeader { func RandomHeader() *FileHeader {
var h FileHeader var h FileHeader
h.Version = CurrentVersion h.Version = CurrentVersion
h.Id = cryptocore.RandBytes(HEADER_ID_LEN) h.ID = cryptocore.RandBytes(headerIDLen)
return &h return &h
} }

View File

@ -1,10 +1,10 @@
package contentenc package contentenc
// intraBlock identifies a part of a file block // IntraBlock identifies a part of a file block
type intraBlock struct { type IntraBlock struct {
// Block number in the file // BlockNo is the block number in the file
BlockNo uint64 BlockNo uint64
// Offset into block payload // Skip is an offset into the block payload
// In forwared mode: block plaintext // In forwared mode: block plaintext
// In reverse mode: offset into block ciphertext. Takes the header into // In reverse mode: offset into block ciphertext. Takes the header into
// account. // account.
@ -17,8 +17,8 @@ type intraBlock struct {
fs *ContentEnc fs *ContentEnc
} }
// isPartial - is the block partial? This means we have to do read-modify-write. // IsPartial - is the block partial? This means we have to do read-modify-write.
func (ib *intraBlock) IsPartial() bool { func (ib *IntraBlock) IsPartial() bool {
if ib.Skip > 0 || ib.Length < ib.fs.plainBS { if ib.Skip > 0 || ib.Length < ib.fs.plainBS {
return true return true
} }
@ -26,17 +26,17 @@ func (ib *intraBlock) IsPartial() bool {
} }
// BlockCipherOff returns the ciphertext offset corresponding to BlockNo // BlockCipherOff returns the ciphertext offset corresponding to BlockNo
func (ib *intraBlock) BlockCipherOff() (offset uint64) { func (ib *IntraBlock) BlockCipherOff() (offset uint64) {
return ib.fs.BlockNoToCipherOff(ib.BlockNo) return ib.fs.BlockNoToCipherOff(ib.BlockNo)
} }
// BlockPlainOff returns the plaintext offset corresponding to BlockNo // BlockPlainOff returns the plaintext offset corresponding to BlockNo
func (ib *intraBlock) BlockPlainOff() (offset uint64) { func (ib *IntraBlock) BlockPlainOff() (offset uint64) {
return ib.fs.BlockNoToPlainOff(ib.BlockNo) return ib.fs.BlockNoToPlainOff(ib.BlockNo)
} }
// CropBlock - crop a potentially larger plaintext block down to the relevant part // CropBlock - crop a potentially larger plaintext block down to the relevant part
func (ib *intraBlock) CropBlock(d []byte) []byte { func (ib *IntraBlock) CropBlock(d []byte) []byte {
lenHave := len(d) lenHave := len(d)
lenWant := int(ib.Skip + ib.Length) lenWant := int(ib.Skip + ib.Length)
if lenHave < lenWant { if lenHave < lenWant {
@ -45,8 +45,9 @@ func (ib *intraBlock) CropBlock(d []byte) []byte {
return d[ib.Skip:lenWant] return d[ib.Skip:lenWant]
} }
// Ciphertext range corresponding to the sum of all "blocks" (complete blocks) // JointCiphertextRange is the ciphertext range corresponding to the sum of all
func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, length uint64) { // "blocks" (complete blocks)
func (ib *IntraBlock) JointCiphertextRange(blocks []IntraBlock) (offset uint64, length uint64) {
firstBlock := blocks[0] firstBlock := blocks[0]
lastBlock := blocks[len(blocks)-1] lastBlock := blocks[len(blocks)-1]
@ -57,8 +58,9 @@ func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64,
return offset, length return offset, length
} }
// Plaintext range corresponding to the sum of all "blocks" (complete blocks) // JointPlaintextRange is the plaintext range corresponding to the sum of all
func JointPlaintextRange(blocks []intraBlock) (offset uint64, length uint64) { // "blocks" (complete blocks)
func JointPlaintextRange(blocks []IntraBlock) (offset uint64, length uint64) {
firstBlock := blocks[0] firstBlock := blocks[0]
lastBlock := blocks[len(blocks)-1] lastBlock := blocks[len(blocks)-1]

View File

@ -8,43 +8,43 @@ import (
// Contentenc methods that translate offsets between ciphertext and plaintext // Contentenc methods that translate offsets between ciphertext and plaintext
// get the block number at plain-text offset // PlainOffToBlockNo converts a plaintext offset to the ciphertext block number.
func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 { func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 {
return plainOffset / be.plainBS return plainOffset / be.plainBS
} }
// get the block number at cipher-text offset // CipherOffToBlockNo converts the ciphertext offset to the plaintext block number.
func (be *ContentEnc) CipherOffToBlockNo(cipherOffset uint64) uint64 { func (be *ContentEnc) CipherOffToBlockNo(cipherOffset uint64) uint64 {
if cipherOffset < HEADER_LEN { if cipherOffset < HeaderLen {
log.Panicf("BUG: offset %d is inside the file header", cipherOffset) log.Panicf("BUG: offset %d is inside the file header", cipherOffset)
} }
return (cipherOffset - HEADER_LEN) / be.cipherBS return (cipherOffset - HeaderLen) / be.cipherBS
} }
// get ciphertext offset of block "blockNo" // BlockNoToCipherOff gets the ciphertext offset of block "blockNo"
func (be *ContentEnc) BlockNoToCipherOff(blockNo uint64) uint64 { func (be *ContentEnc) BlockNoToCipherOff(blockNo uint64) uint64 {
return HEADER_LEN + blockNo*be.cipherBS return HeaderLen + blockNo*be.cipherBS
} }
// get plaintext offset of block "blockNo" // BlockNoToPlainOff gets the plaintext offset of block "blockNo"
func (be *ContentEnc) BlockNoToPlainOff(blockNo uint64) uint64 { func (be *ContentEnc) BlockNoToPlainOff(blockNo uint64) uint64 {
return blockNo * be.plainBS return blockNo * be.plainBS
} }
// PlainSize - calculate plaintext size from ciphertext size // CipherSizeToPlainSize calculates the plaintext size from a ciphertext size
func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {
// Zero-sized files stay zero-sized // Zero-sized files stay zero-sized
if cipherSize == 0 { if cipherSize == 0 {
return 0 return 0
} }
if cipherSize == HEADER_LEN { if cipherSize == HeaderLen {
tlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize) tlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)
return 0 return 0
} }
if cipherSize < HEADER_LEN { if cipherSize < HeaderLen {
tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HEADER_LEN) tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HeaderLen)
return 0 return 0
} }
@ -52,12 +52,12 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {
blockNo := be.CipherOffToBlockNo(cipherSize - 1) blockNo := be.CipherOffToBlockNo(cipherSize - 1)
blockCount := blockNo + 1 blockCount := blockNo + 1
overhead := be.BlockOverhead()*blockCount + HEADER_LEN overhead := be.BlockOverhead()*blockCount + HeaderLen
return cipherSize - overhead return cipherSize - overhead
} }
// CipherSize - calculate ciphertext size from plaintext size // PlainSizeToCipherSize calculates the ciphertext size from a plaintext size
func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 { func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {
// Zero-sized files stay zero-sized // Zero-sized files stay zero-sized
if plainSize == 0 { if plainSize == 0 {
@ -68,16 +68,16 @@ func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {
blockNo := be.PlainOffToBlockNo(plainSize - 1) blockNo := be.PlainOffToBlockNo(plainSize - 1)
blockCount := blockNo + 1 blockCount := blockNo + 1
overhead := be.BlockOverhead()*blockCount + HEADER_LEN overhead := be.BlockOverhead()*blockCount + HeaderLen
return plainSize + overhead return plainSize + overhead
} }
// Split a plaintext byte range into (possibly partial) blocks // ExplodePlainRange splits a plaintext byte range into (possibly partial) blocks
// Returns an empty slice if length == 0. // Returns an empty slice if length == 0.
func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlock { func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []IntraBlock {
var blocks []intraBlock var blocks []IntraBlock
var nextBlock intraBlock var nextBlock IntraBlock
nextBlock.fs = be nextBlock.fs = be
for length > 0 { for length > 0 {
@ -94,11 +94,11 @@ func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlo
return blocks return blocks
} }
// Split a ciphertext byte range into (possibly partial) blocks // ExplodeCipherRange splits a ciphertext byte range into (possibly partial)
// This is used in reverse mode when reading files // blocks This is used in reverse mode when reading files
func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBlock { func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []IntraBlock {
var blocks []intraBlock var blocks []IntraBlock
var nextBlock intraBlock var nextBlock IntraBlock
nextBlock.fs = be nextBlock.fs = be
for length > 0 { for length > 0 {
@ -120,10 +120,12 @@ func (be *ContentEnc) ExplodeCipherRange(offset uint64, length uint64) []intraBl
return blocks return blocks
} }
// BlockOverhead returns the per-block overhead.
func (be *ContentEnc) BlockOverhead() uint64 { func (be *ContentEnc) BlockOverhead() uint64 {
return be.cipherBS - be.plainBS return be.cipherBS - be.plainBS
} }
// MinUint64 returns the minimum of two uint64 values.
func MinUint64(x uint64, y uint64) uint64 { func MinUint64(x uint64, y uint64) uint64 {
if x < y { if x < y {
return x return x

View File

@ -12,18 +12,25 @@ import (
"github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/stupidgcm"
) )
// BackendTypeEnum indicates the type of backend in use.
type BackendTypeEnum int type BackendTypeEnum int
const ( const (
KeyLen = 32 // AES-256 // KeyLen is the cipher key length in bytes. 32 for AES-256.
KeyLen = 32
// AuthTagLen is the length of a GCM auth tag in bytes.
AuthTagLen = 16 AuthTagLen = 16
_ = iota // Skip zero _ = iota // Skip zero
// BackendOpenSSL specifies the OpenSSL backend.
BackendOpenSSL BackendTypeEnum = iota BackendOpenSSL BackendTypeEnum = iota
BackendGoGCM BackendTypeEnum = iota // BackendGoGCM specifies the Go based GCM backend.
BackendAESSIV BackendTypeEnum = iota BackendGoGCM BackendTypeEnum = iota
// BackendAESSIV specifies an AESSIV backend.
BackendAESSIV BackendTypeEnum = iota
) )
// CryptoCore is the low level crypto implementation.
type CryptoCore struct { type CryptoCore struct {
// AES-256 block cipher. This is used for EME filename encryption. // AES-256 block cipher. This is used for EME filename encryption.
BlockCipher cipher.Block BlockCipher cipher.Block
@ -36,7 +43,7 @@ type CryptoCore struct {
IVLen int IVLen int
} }
// "New" returns a new CryptoCore object or panics. // New returns a new CryptoCore object or panics.
// //
// Even though the "GCMIV128" feature flag is now mandatory, we must still // Even though the "GCMIV128" feature flag is now mandatory, we must still
// support 96-bit IVs here because they are used for encrypting the master // support 96-bit IVs here because they are used for encrypting the master

View File

@ -10,7 +10,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// Get "n" random bytes from /dev/urandom or panic // RandBytes gets "n" random bytes from /dev/urandom or panics
func RandBytes(n int) []byte { func RandBytes(n int) []byte {
b := make([]byte, n) b := make([]byte, n)
_, err := rand.Read(b) _, err := rand.Read(b)
@ -20,7 +20,7 @@ func RandBytes(n int) []byte {
return b return b
} }
// Return a secure random uint64 // RandUint64 returns a secure random uint64
func RandUint64() uint64 { func RandUint64() uint64 {
b := RandBytes(8) b := RandBytes(8)
return binary.BigEndian.Uint64(b) return binary.BigEndian.Uint64(b)

View File

@ -4,7 +4,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/cryptocore"
) )
// Container for arguments that are passed from main() to fusefrontend // Args is a container for arguments that are passed from main() to fusefrontend
type Args struct { type Args struct {
Masterkey []byte Masterkey []byte
Cipherdir string Cipherdir string

View File

@ -45,6 +45,7 @@ type file struct {
loopbackFile nodefs.File loopbackFile nodefs.File
} }
// NewFile returns a new go-fuse File instance.
func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) { func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) {
var st syscall.Stat_t var st syscall.Stat_t
err := syscall.Fstat(int(fd.Fd()), &st) err := syscall.Fstat(int(fd.Fd()), &st)
@ -80,7 +81,7 @@ func (f *file) SetInode(n *nodefs.Inode) {
// //
// Returns io.EOF if the file is empty // Returns io.EOF if the file is empty
func (f *file) readHeader() error { func (f *file) readHeader() error {
buf := make([]byte, contentenc.HEADER_LEN) buf := make([]byte, contentenc.HeaderLen)
_, err := f.fd.ReadAt(buf, 0) _, err := f.fd.ReadAt(buf, 0)
if err != nil { if err != nil {
return err return err
@ -100,7 +101,7 @@ func (f *file) createHeader() error {
buf := h.Pack() buf := h.Pack()
// Prevent partially written (=corrupt) header by preallocating the space beforehand // Prevent partially written (=corrupt) header by preallocating the space beforehand
err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HEADER_LEN) err := syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen)
if err != nil { if err != nil {
tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error()) tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())
return err return err
@ -159,7 +160,7 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {
tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n) tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)
// Decrypt it // Decrypt it
plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id) plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.ID)
if err != nil { if err != nil {
curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext)))
cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo) cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo)
@ -256,7 +257,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
// Encrypt // Encrypt
blockOffset := b.BlockCipherOff() blockOffset := b.BlockCipherOff()
blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id) blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.ID)
tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d", tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",
f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo) f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)

View File

@ -14,7 +14,10 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// FALLOC_DEFAULT is a "normal" fallocate operation
const FALLOC_DEFAULT = 0x00 const FALLOC_DEFAULT = 0x00
// FALLOC_FL_KEEP_SIZE allocates disk space while not modifying the file size
const FALLOC_FL_KEEP_SIZE = 0x01 const FALLOC_FL_KEEP_SIZE = 0x01
// Only warn once // Only warn once
@ -116,11 +119,12 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
oldSize, err := f.statPlainSize() oldSize, err := f.statPlainSize()
if err != nil { if err != nil {
return fuse.ToStatus(err) return fuse.ToStatus(err)
} else {
oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
newB := float32(newSize) / float32(f.contentEnc.PlainBS())
tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize)
} }
oldB := float32(oldSize) / float32(f.contentEnc.PlainBS())
newB := float32(newSize) / float32(f.contentEnc.PlainBS())
tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize)
// File size stays the same - nothing to do // File size stays the same - nothing to do
if newSize == oldSize { if newSize == oldSize {
return fuse.OK return fuse.OK
@ -128,34 +132,34 @@ func (f *file) Truncate(newSize uint64) fuse.Status {
// File grows // File grows
if newSize > oldSize { if newSize > oldSize {
return f.truncateGrowFile(oldSize, newSize) return f.truncateGrowFile(oldSize, newSize)
} else { }
// File shrinks
blockNo := f.contentEnc.PlainOffToBlockNo(newSize) // File shrinks
cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) blockNo := f.contentEnc.PlainOffToBlockNo(newSize)
plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo)
lastBlockLen := newSize - plainOff plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)
var data []byte lastBlockLen := newSize - plainOff
if lastBlockLen > 0 { var data []byte
var status fuse.Status if lastBlockLen > 0 {
data, status = f.doRead(plainOff, lastBlockLen) var status fuse.Status
if status != fuse.OK { data, status = f.doRead(plainOff, lastBlockLen)
tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) if status != fuse.OK {
return status tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err)
}
}
// Truncate down to the last complete block
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
if err != nil {
tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err)
return fuse.ToStatus(err)
}
// Append partial block
if lastBlockLen > 0 {
_, status := f.doWrite(data, int64(plainOff))
return status return status
} }
return fuse.OK
} }
// Truncate down to the last complete block
err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))
if err != nil {
tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err)
return fuse.ToStatus(err)
}
// Append partial block
if lastBlockLen > 0 {
_, status := f.doWrite(data, int64(plainOff))
return status
}
return fuse.OK
} }
// statPlainSize stats the file and returns the plaintext size // statPlainSize stats the file and returns the plaintext size
@ -198,12 +202,12 @@ func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Statu
off := lastBlock.BlockPlainOff() off := lastBlock.BlockPlainOff()
_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) _, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip))
return status return status
} else {
off := lastBlock.BlockCipherOff()
err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS()))
if err != nil {
tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err)
}
return fuse.ToStatus(err)
} }
off := lastBlock.BlockCipherOff()
err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS()))
if err != nil {
tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err)
}
return fuse.ToStatus(err)
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// FS implements the go-fuse virtual filesystem interface.
type FS struct { type FS struct {
pathfs.FileSystem // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go pathfs.FileSystem // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go
args Args // Stores configuration arguments args Args // Stores configuration arguments
@ -35,7 +36,9 @@ type FS struct {
contentEnc *contentenc.ContentEnc contentEnc *contentenc.ContentEnc
} }
// Encrypted FUSE overlay filesystem var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented.
// NewFS returns a new encrypted FUSE overlay filesystem.
func NewFS(args Args) *FS { func NewFS(args Args) *FS {
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
@ -49,6 +52,7 @@ func NewFS(args Args) *FS {
} }
} }
// GetAttr implements pathfs.Filesystem.
func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
tlog.Debug.Printf("FS.GetAttr('%s')", name) tlog.Debug.Printf("FS.GetAttr('%s')", name)
if fs.isFiltered(name) { if fs.isFiltered(name) {
@ -85,6 +89,7 @@ func (fs *FS) mangleOpenFlags(flags uint32) (newFlags int, writeOnly bool) {
return newFlags, writeOnly return newFlags, writeOnly
} }
// Open implements pathfs.Filesystem.
func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return nil, fuse.EPERM return nil, fuse.EPERM
@ -104,6 +109,7 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n
return NewFile(f, writeOnly, fs.contentEnc) return NewFile(f, writeOnly, fs.contentEnc)
} }
// Create implements pathfs.Filesystem.
func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return nil, fuse.EPERM return nil, fuse.EPERM
@ -156,6 +162,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte
return NewFile(fd, writeOnly, fs.contentEnc) return NewFile(fd, writeOnly, fs.contentEnc)
} }
// Chmod implements pathfs.Filesystem.
func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -170,6 +177,7 @@ func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
// Chown implements pathfs.Filesystem.
func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -181,6 +189,7 @@ func (fs *FS) Chown(path string, uid uint32, gid uint32, context *fuse.Context)
return fuse.ToStatus(os.Lchown(cPath, int(uid), int(gid))) return fuse.ToStatus(os.Lchown(cPath, int(uid), int(gid)))
} }
// Mknod implements pathfs.Filesystem.
func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -217,6 +226,7 @@ func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context)
return fs.FileSystem.Mknod(cPath, mode, dev, context) return fs.FileSystem.Mknod(cPath, mode, dev, context)
} }
// Truncate implements pathfs.Filesystem.
// Support truncate(2) by opening the file and calling ftruncate(2) // Support truncate(2) by opening the file and calling ftruncate(2)
// While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from // While the glibc "truncate" wrapper seems to always use ftruncate, fsstress from
// xfstests uses this a lot by calling "truncate64" directly. // xfstests uses this a lot by calling "truncate64" directly.
@ -230,6 +240,7 @@ func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code
return code return code
} }
// Utimens implements pathfs.Filesystem.
func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) { func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -241,6 +252,7 @@ func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Con
return fs.FileSystem.Utimens(cPath, a, m, context) return fs.FileSystem.Utimens(cPath, a, m, context)
} }
// StatFs implements pathfs.Filesystem.
func (fs *FS) StatFs(path string) *fuse.StatfsOut { func (fs *FS) StatFs(path string) *fuse.StatfsOut {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return nil return nil
@ -252,6 +264,7 @@ func (fs *FS) StatFs(path string) *fuse.StatfsOut {
return fs.FileSystem.StatFs(cPath) return fs.FileSystem.StatFs(cPath)
} }
// Readlink implements pathfs.Filesystem.
func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) { func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status fuse.Status) {
cPath, err := fs.getBackingPath(path) cPath, err := fs.getBackingPath(path)
if err != nil { if err != nil {
@ -278,6 +291,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f
return string(target), fuse.OK return string(target), fuse.OK
} }
// Unlink implements pathfs.Filesystem.
func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) { func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -312,6 +326,7 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
// Symlink implements pathfs.Filesystem.
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) tlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)
if fs.isFiltered(linkName) { if fs.isFiltered(linkName) {
@ -359,6 +374,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co
return fuse.ToStatus(err) return fuse.ToStatus(err)
} }
// Rename implements pathfs.Filesystem.
func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(newPath) { if fs.isFiltered(newPath) {
return fuse.EPERM return fuse.EPERM
@ -437,6 +453,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
return fuse.OK return fuse.OK
} }
// Link implements pathfs.Filesystem.
func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) { func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(newPath) { if fs.isFiltered(newPath) {
return fuse.EPERM return fuse.EPERM
@ -474,6 +491,7 @@ func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code
return fuse.ToStatus(os.Link(cOldPath, cNewPath)) return fuse.ToStatus(os.Link(cOldPath, cNewPath))
} }
// Access implements pathfs.Filesystem.
func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) { func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(path) { if fs.isFiltered(path) {
return fuse.EPERM return fuse.EPERM
@ -485,18 +503,22 @@ func (fs *FS) Access(path string, mode uint32, context *fuse.Context) (code fuse
return fuse.ToStatus(syscall.Access(cPath, mode)) return fuse.ToStatus(syscall.Access(cPath, mode))
} }
// GetXAttr implements pathfs.Filesystem.
func (fs *FS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { func (fs *FS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
return nil, fuse.ENOSYS return nil, fuse.ENOSYS
} }
// SetXAttr implements pathfs.Filesystem.
func (fs *FS) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { func (fs *FS) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
return fuse.ENOSYS return fuse.ENOSYS
} }
// ListXAttr implements pathfs.Filesystem.
func (fs *FS) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) { func (fs *FS) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
return nil, fuse.ENOSYS return nil, fuse.ENOSYS
} }
// RemoveXAttr implements pathfs.Filesystem.
func (fs *FS) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status { func (fs *FS) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
return fuse.ENOSYS return fuse.ENOSYS
} }

View File

@ -40,6 +40,7 @@ func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
return err return err
} }
// Mkdir implements pathfs.FileSystem
func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) { func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
if fs.isFiltered(newPath) { if fs.isFiltered(newPath) {
return fuse.EPERM return fuse.EPERM
@ -97,6 +98,7 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
return fuse.OK return fuse.OK
} }
// Rmdir implements pathfs.FileSystem
func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
cPath, err := fs.getBackingPath(path) cPath, err := fs.getBackingPath(path)
if err != nil { if err != nil {
@ -215,6 +217,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
return fuse.OK return fuse.OK
} }
// OpenDir implements pathfs.FileSystem
func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
tlog.Debug.Printf("OpenDir(%s)", dirName) tlog.Debug.Printf("OpenDir(%s)", dirName)
cDirName, err := fs.encryptPath(dirName) cDirName, err := fs.encryptPath(dirName)

View File

@ -4,7 +4,7 @@ import (
"sync/atomic" "sync/atomic"
) )
func NewInoGen() *inoGenT { func newInoGen() *inoGenT {
var ino uint64 = 1 var ino uint64 = 1
return &inoGenT{&ino} return &inoGenT{&ino}
} }

View File

@ -70,9 +70,9 @@ func (rfs *reverseFS) findLongnameParent(dir string, dirIV []byte, longname stri
} }
if hit == "" { if hit == "" {
return "", syscall.ENOENT return "", syscall.ENOENT
} else {
return hit, nil
} }
return hit, nil
} }
func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) { func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {

View File

@ -39,7 +39,7 @@ func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.S
id := derivePathIV(relPath, ivPurposeFileID) id := derivePathIV(relPath, ivPurposeFileID)
header := contentenc.FileHeader{ header := contentenc.FileHeader{
Version: contentenc.CurrentVersion, Version: contentenc.CurrentVersion,
Id: id, ID: id,
} }
return &reverseFile{ return &reverseFile{
File: nodefs.NewDefaultFile(), File: nodefs.NewDefaultFile(),
@ -58,7 +58,7 @@ func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status {
// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks. // encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks.
// "plaintext" must already be block-aligned. // "plaintext" must already be block-aligned.
func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileId []byte, block0IV []byte) []byte { func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileID []byte, block0IV []byte) []byte {
nonce := make([]byte, len(block0IV)) nonce := make([]byte, len(block0IV))
copy(nonce, block0IV) copy(nonce, block0IV)
block0IVlow := binary.BigEndian.Uint64(block0IV[8:]) block0IVlow := binary.BigEndian.Uint64(block0IV[8:])
@ -70,7 +70,7 @@ func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, file
for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ { for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ {
binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo) binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo)
inBlock := inBuf.Next(bs) inBlock := inBuf.Next(bs)
outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileId, nonce) outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileID, nonce)
outBuf.Write(outBlock) outBuf.Write(outBlock)
} }
return outBuf.Bytes() return outBuf.Bytes()
@ -95,7 +95,7 @@ func (rf *reverseFile) readBackingFile(off uint64, length uint64) (out []byte, e
plaintext = plaintext[0:n] plaintext = plaintext[0:n]
// Encrypt blocks // Encrypt blocks
ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.Id, rf.block0IV) ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.ID, rf.block0IV)
// Crop down to the relevant part // Crop down to the relevant part
lenHave := len(ciphertext) lenHave := len(ciphertext)
@ -118,7 +118,7 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult,
var header []byte var header []byte
// Synthesize file header // Synthesize file header
if off < contentenc.HEADER_LEN { if off < contentenc.HeaderLen {
header = rf.header.Pack() header = rf.header.Pack()
// Truncate to requested part // Truncate to requested part
end := int(off) + len(buf) end := int(off) + len(buf)

View File

@ -20,6 +20,7 @@ import (
) )
const ( const (
// DirIVMode is the mode to use for Dir IV files.
DirIVMode = syscall.S_IFREG | 0400 DirIVMode = syscall.S_IFREG | 0400
) )
@ -42,8 +43,10 @@ type reverseFS struct {
inoMapLock sync.Mutex inoMapLock sync.Mutex
} }
// Encrypted FUSE overlay filesystem var _ pathfs.FileSystem = &reverseFS{}
func NewFS(args fusefrontend.Args) *reverseFS {
// NewFS returns an encrypted FUSE overlay filesystem
func NewFS(args fusefrontend.Args) pathfs.FileSystem {
cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits) cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits)
contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)
nameTransform := nametransform.New(cryptoCore, args.LongNames) nameTransform := nametransform.New(cryptoCore, args.LongNames)
@ -55,7 +58,7 @@ func NewFS(args fusefrontend.Args) *reverseFS {
args: args, args: args,
nameTransform: nameTransform, nameTransform: nameTransform,
contentEnc: contentEnc, contentEnc: contentEnc,
inoGen: NewInoGen(), inoGen: newInoGen(),
inoMap: map[devIno]uint64{}, inoMap: map[devIno]uint64{},
} }
} }

View File

@ -14,10 +14,10 @@ import (
) )
const ( const (
// identical to AES block size // DirIVLen is identical to AES block size
DirIVLen = 16 DirIVLen = 16
// dirIV is stored in this file. Exported because we have to ignore this // DirIVFilename is the filename used to store directory IV.
// name in directory listing. // Exported because we have to ignore this name in directory listing.
DirIVFilename = "gocryptfs.diriv" DirIVFilename = "gocryptfs.diriv"
) )

View File

@ -14,6 +14,7 @@ import (
) )
const ( const (
// LongNameSuffix is the suffix used for files with long names.
// Files with long names are stored in two files: // Files with long names are stored in two files:
// gocryptfs.longname.[sha256] <--- File content, prefix = gocryptfs.longname. // gocryptfs.longname.[sha256] <--- File content, prefix = gocryptfs.longname.
// gocryptfs.longname.[sha256].name <--- File name, suffix = .name // gocryptfs.longname.[sha256].name <--- File name, suffix = .name
@ -31,12 +32,13 @@ func HashLongName(name string) string {
// Values returned by IsLongName // Values returned by IsLongName
const ( const (
// File that stores the file content. // LongNameContent is the file that stores the file content.
// Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro= // Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=
LongNameContent = iota LongNameContent = iota
// File that stores the full encrypted filename. // LongNameFilename is the file that stores the full encrypted filename.
// Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=.name // Example: gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=.name
LongNameFilename = iota LongNameFilename = iota
// LongNameNone is used when the file does not have a long name.
// Example: i1bpTaVLZq7sRNA9mL_2Ig== // Example: i1bpTaVLZq7sRNA9mL_2Ig==
LongNameNone = iota LongNameNone = iota
) )

View File

@ -1,4 +1,4 @@
// Package namtransforms encrypts and decrypts filenames. // Package nametransform encrypts and decrypts filenames.
package nametransform package nametransform
import ( import (
@ -12,12 +12,14 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// NameTransform is used to transform filenames.
type NameTransform struct { type NameTransform struct {
cryptoCore *cryptocore.CryptoCore cryptoCore *cryptocore.CryptoCore
longNames bool longNames bool
DirIVCache dirIVCache DirIVCache dirIVCache
} }
// New returns a new NameTransform instance.
func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform { func New(c *cryptocore.CryptoCore, longNames bool) *NameTransform {
return &NameTransform{ return &NameTransform{
cryptoCore: c, cryptoCore: c,
@ -51,7 +53,7 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error
return plain, err return plain, err
} }
// encryptName - encrypt "plainName", return base64-encoded "cipherName64". // EncryptName encrypts "plainName", returns a base64-encoded "cipherName64".
// Used internally by EncryptPathDirIV(). // Used internally by EncryptPathDirIV().
// The encryption is either CBC or EME, depending on "useEME". // The encryption is either CBC or EME, depending on "useEME".
// //

View File

@ -7,6 +7,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/stupidgcm"
) )
// PreferOpenSSL returns true if OpenSSL should be used.
func PreferOpenSSL() bool { func PreferOpenSSL() bool {
if stupidgcm.BuiltWithoutOpenssl { if stupidgcm.BuiltWithoutOpenssl {
return false return false

View File

@ -17,8 +17,8 @@ const (
exitCode = 9 exitCode = 9
) )
// Once() tries to get a password from the user, either from the terminal, // Once tries to get a password from the user, either from the terminal, extpass
// extpass or stdin. // or stdin.
func Once(extpass string) string { func Once(extpass string) string {
if extpass != "" { if extpass != "" {
return readPasswordExtpass(extpass) return readPasswordExtpass(extpass)
@ -29,8 +29,8 @@ func Once(extpass string) string {
return readPasswordTerminal("Password: ") return readPasswordTerminal("Password: ")
} }
// Twice() is the same as Once but will prompt twice if we get // Twice is the same as Once but will prompt twice if we get the password from
// the password from the terminal. // the terminal.
func Twice(extpass string) string { func Twice(extpass string) string {
if extpass != "" { if extpass != "" {
return readPasswordExtpass(extpass) return readPasswordExtpass(extpass)

View File

@ -3,6 +3,8 @@
package siv_aead package siv_aead
import ( import (
"crypto/cipher"
"github.com/jacobsa/crypto/siv" "github.com/jacobsa/crypto/siv"
) )
@ -10,7 +12,10 @@ type sivAead struct {
key []byte key []byte
} }
func New(key []byte) *sivAead { var _ cipher.AEAD = &sivAead{}
// New returns a new cipher.AEAD implementation.
func New(key []byte) cipher.AEAD {
return &sivAead{ return &sivAead{
key: key, key: key,
} }
@ -25,7 +30,7 @@ func (s *sivAead) Overhead() int {
return 16 return 16
} }
// Seal - encrypt "in" using "nonce" and "authData" and append the result to "dst" // Seal encrypts "in" using "nonce" and "authData" and append the result to "dst"
func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte { func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
if len(nonce) != 16 { if len(nonce) != 16 {
// SIV supports any nonce size, but in gocryptfs we exclusively use 16. // SIV supports any nonce size, but in gocryptfs we exclusively use 16.
@ -43,7 +48,7 @@ func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
return out return out
} }
// Open - decrypt "in" using "nonce" and "authData" and append the result to "dst" // Open decrypts "in" using "nonce" and "authData" and append the result to "dst"
func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) { func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) {
if len(nonce) != 16 { if len(nonce) != 16 {
// SIV supports any nonce size, but in gocryptfs we exclusively use 16. // SIV supports any nonce size, but in gocryptfs we exclusively use 16.

View File

@ -9,6 +9,7 @@ package stupidgcm
import "C" import "C"
import ( import (
"crypto/cipher"
"fmt" "fmt"
"log" "log"
"unsafe" "unsafe"
@ -28,7 +29,10 @@ type stupidGCM struct {
key []byte key []byte
} }
func New(key []byte) stupidGCM { var _ cipher.AEAD = &stupidGCM{}
// New returns a new cipher.AEAD implementation..
func New(key []byte) cipher.AEAD {
if len(key) != keyLen { if len(key) != keyLen {
log.Panicf("Only %d-byte keys are supported", keyLen) log.Panicf("Only %d-byte keys are supported", keyLen)
} }
@ -43,7 +47,7 @@ func (g stupidGCM) Overhead() int {
return tagLen return tagLen
} }
// Seal - encrypt "in" using "iv" and "authData" and append the result to "dst" // Seal encrypts "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte { func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
if len(iv) != ivLen { if len(iv) != ivLen {
log.Panicf("Only %d-byte IVs are supported", ivLen) log.Panicf("Only %d-byte IVs are supported", ivLen)
@ -114,7 +118,7 @@ func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
return append(dst, buf...) return append(dst, buf...)
} }
// Open - decrypt "in" using "iv" and "authData" and append the result to "dst" // Open decrypts "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
if len(iv) != ivLen { if len(iv) != ivLen {
log.Panicf("Only %d-byte IVs are supported", ivLen) log.Panicf("Only %d-byte IVs are supported", ivLen)

View File

@ -38,22 +38,27 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) {
} }
} }
// Fallocate wraps the Fallocate syscall.
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) { func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
return syscall.Fallocate(fd, mode, off, len) return syscall.Fallocate(fd, mode, off, len)
} }
// Openat wraps the Openat syscall.
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
return syscall.Openat(dirfd, path, flags, mode) return syscall.Openat(dirfd, path, flags, mode)
} }
// Renameat wraps the Renameat syscall.
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath) return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath)
} }
// Unlinkat wraps the Unlinkat syscall.
func Unlinkat(dirfd int, path string) error { func Unlinkat(dirfd int, path string) error {
return syscall.Unlinkat(dirfd, path) return syscall.Unlinkat(dirfd, path)
} }
// Mknodat wraps the Mknodat syscall.
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
return syscall.Mknodat(dirfd, path, mode, dev) return syscall.Mknodat(dirfd, path, mode, dev)
} }

View File

@ -12,21 +12,34 @@ import (
) )
const ( const (
// ProgramName is used in log reports.
ProgramName = "gocryptfs" ProgramName = "gocryptfs"
wpanicMsg = "-wpanic turns this warning into a panic: " wpanicMsg = "-wpanic turns this warning into a panic: "
) )
// Escape sequences for terminal colors. These will be empty strings if stdout // Escape sequences for terminal colors. These will be empty strings if stdout
// is not a terminal. // is not a terminal.
var ColorReset, ColorGrey, ColorRed, ColorGreen, ColorYellow string var (
// ColorReset is used to reset terminal colors.
ColorReset string
// ColorGrey is a terminal color setting string.
ColorGrey string
// ColorRed is a terminal color setting string.
ColorRed string
// ColorGreen is a terminal color setting string.
ColorGreen string
// ColorYellow is a terminal color setting string.
ColorYellow string
)
// JSONDump writes the object in json form.
func JSONDump(obj interface{}) string { func JSONDump(obj interface{}) string {
b, err := json.MarshalIndent(obj, "", "\t") b, err := json.MarshalIndent(obj, "", "\t")
if err != nil { if err != nil {
return err.Error() return err.Error()
} else {
return string(b)
} }
return string(b)
} }
// toggledLogger - a Logger than can be enabled and disabled // toggledLogger - a Logger than can be enabled and disabled

50
main.go
View File

@ -29,25 +29,25 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
// Exit codes
const ( const (
// Exit codes ErrExitUsage = 1
ERREXIT_USAGE = 1 ErrExitMount = 3
ERREXIT_MOUNT = 3 ErrExitCipherDir = 6
ERREXIT_CIPHERDIR = 6 ErrExitInit = 7
ERREXIT_INIT = 7 ErrExitLoadConf = 8
ERREXIT_LOADCONF = 8 ErrExitMountPoint = 10
ERREXIT_MOUNTPOINT = 10
) )
const pleaseBuildBash = "[not set - please compile using ./build.bash]" const pleaseBuildBash = "[not set - please compile using ./build.bash]"
// gocryptfs version according to git, set by build.bash // GitVersion is the gocryptfs version according to git, set by build.bash
var GitVersion = pleaseBuildBash var GitVersion = pleaseBuildBash
// go-fuse library version, set by build.bash // GitVersionFuse is the go-fuse library version, set by build.bash
var GitVersionFuse = pleaseBuildBash var GitVersionFuse = pleaseBuildBash
// Unix timestamp, set by build.bash // BuildTime is the Unix timestamp, set by build.bash
var BuildTime = "0" var BuildTime = "0"
func usageText() { func usageText() {
@ -68,14 +68,14 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.Conf
_, err := os.Stat(args.config) _, err := os.Stat(args.config)
if err != nil { if err != nil {
tlog.Fatal.Printf("Config file not found: %v", err) tlog.Fatal.Printf("Config file not found: %v", err)
os.Exit(ERREXIT_LOADCONF) os.Exit(ErrExitLoadConf)
} }
pw := readpassword.Once(args.extpass) pw := readpassword.Once(args.extpass)
tlog.Info.Println("Decrypting master key") tlog.Info.Println("Decrypting master key")
masterkey, confFile, err = configfile.LoadConfFile(args.config, pw) masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_LOADCONF) os.Exit(ErrExitLoadConf)
} }
return masterkey, confFile return masterkey, confFile
@ -90,7 +90,7 @@ func changePassword(args *argContainer) {
err := confFile.WriteFile() err := confFile.WriteFile()
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
tlog.Info.Printf("Password changed.") tlog.Info.Printf("Password changed.")
os.Exit(0) os.Exit(0)
@ -145,11 +145,11 @@ func main() {
err = checkDir(args.cipherdir) err = checkDir(args.cipherdir)
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid cipherdir: %v", err) tlog.Fatal.Printf("Invalid cipherdir: %v", err)
os.Exit(ERREXIT_CIPHERDIR) os.Exit(ErrExitCipherDir)
} }
} else { } else {
usageText() usageText()
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
// "-q" // "-q"
if args.quiet { if args.quiet {
@ -164,7 +164,7 @@ func main() {
args.config, err = filepath.Abs(args.config) args.config, err = filepath.Abs(args.config)
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err) tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
tlog.Info.Printf("Using config file at custom location %s", args.config) tlog.Info.Printf("Using config file at custom location %s", args.config)
} else if args.reverse { } else if args.reverse {
@ -179,7 +179,7 @@ func main() {
f, err = os.Create(args.cpuprofile) f, err = os.Create(args.cpuprofile)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
pprof.StartCPUProfile(f) pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
@ -191,7 +191,7 @@ func main() {
f, err = os.Create(args.memprofile) f, err = os.Create(args.memprofile)
if err != nil { if err != nil {
tlog.Fatal.Println(err) tlog.Fatal.Println(err)
os.Exit(ERREXIT_INIT) os.Exit(ErrExitInit)
} }
defer func() { defer func() {
pprof.WriteHeapProfile(f) pprof.WriteHeapProfile(f)
@ -213,7 +213,7 @@ func main() {
if args.init { if args.init {
if flagSet.NArg() > 1 { if flagSet.NArg() > 1 {
tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName) tlog.Fatal.Printf("Usage: %s -init [OPTIONS] CIPHERDIR", tlog.ProgramName)
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
initDir(&args) // does not return initDir(&args) // does not return
} }
@ -221,7 +221,7 @@ func main() {
if args.passwd { if args.passwd {
if flagSet.NArg() > 1 { if flagSet.NArg() > 1 {
tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName) tlog.Fatal.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR", tlog.ProgramName)
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
changePassword(&args) // does not return changePassword(&args) // does not return
} }
@ -229,17 +229,17 @@ func main() {
// Check mountpoint // Check mountpoint
if flagSet.NArg() != 2 { if flagSet.NArg() != 2 {
tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName) tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT", tlog.ProgramName)
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
args.mountpoint, err = filepath.Abs(flagSet.Arg(1)) args.mountpoint, err = filepath.Abs(flagSet.Arg(1))
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid mountpoint: %v", err) tlog.Fatal.Printf("Invalid mountpoint: %v", err)
os.Exit(ERREXIT_MOUNTPOINT) os.Exit(ErrExitMountPoint)
} }
err = checkDirEmpty(args.mountpoint) err = checkDirEmpty(args.mountpoint)
if err != nil { if err != nil {
tlog.Fatal.Printf("Invalid mountpoint: %v", err) tlog.Fatal.Printf("Invalid mountpoint: %v", err)
os.Exit(ERREXIT_MOUNTPOINT) os.Exit(ErrExitMountPoint)
} }
// Get master key // Get master key
var masterkey []byte var masterkey []byte
@ -313,7 +313,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
frontendArgs.CryptoBackend = cryptocore.BackendAESSIV frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
} else if args.reverse { } else if args.reverse {
tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file") tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
os.Exit(ERREXIT_USAGE) os.Exit(ErrExitUsage)
} }
} }
// If allow_other is set and we run as root, try to give newly created files to // If allow_other is set and we run as root, try to give newly created files to
@ -372,7 +372,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts) srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
if err != nil { if err != nil {
tlog.Fatal.Printf("Mount failed: %v", err) tlog.Fatal.Printf("Mount failed: %v", err)
os.Exit(ERREXIT_MOUNT) os.Exit(ErrExitMount)
} }
srv.SetDebug(args.fusedebug) srv.SetDebug(args.fusedebug)

View File

@ -17,16 +17,18 @@ import (
// TmpDir will be created inside this directory // TmpDir will be created inside this directory
const testParentDir = "/tmp/gocryptfs-test-parent" const testParentDir = "/tmp/gocryptfs-test-parent"
// GocryptfsBinary is the assumed path to the gocryptfs build.
const GocryptfsBinary = "../../gocryptfs" const GocryptfsBinary = "../../gocryptfs"
// "go test" runs package tests in parallel! We create a unique TmpDir in // TmpDir is a unique temporary directory. "go test" runs package tests in parallel. We create a
// init() so the tests do not interfere. // unique TmpDir in init() so the tests do not interfere.
var TmpDir string var TmpDir string
// TmpDir + "/default-plain" // DefaultPlainDir is TmpDir + "/default-plain"
var DefaultPlainDir string var DefaultPlainDir string
// TmpDir + "/default-cipher" // DefaultCipherDir is TmpDir + "/default-cipher"
var DefaultCipherDir string var DefaultCipherDir string
func init() { func init() {
@ -40,7 +42,7 @@ func init() {
DefaultCipherDir = TmpDir + "/default-cipher" DefaultCipherDir = TmpDir + "/default-cipher"
} }
// ResetTmpDir - delete TmpDir, create new dir tree: // ResetTmpDir deletes TmpDir, create new dir tree:
// //
// TmpDir // TmpDir
// |-- DefaultPlainDir // |-- DefaultPlainDir
@ -167,7 +169,7 @@ func UnmountPanic(dir string) {
} }
} }
// UnmountError tries to unmount "dir" and returns the resulting error. // UnmountErr tries to unmount "dir" and returns the resulting error.
func UnmountErr(dir string) error { func UnmountErr(dir string) error {
var cmd *exec.Cmd var cmd *exec.Cmd
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
@ -180,7 +182,7 @@ func UnmountErr(dir string) error {
return cmd.Run() return cmd.Run()
} }
// Return md5 string for file "filename" // Md5fn returns an md5 string for file "filename"
func Md5fn(filename string) string { func Md5fn(filename string) string {
buf, err := ioutil.ReadFile(filename) buf, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
@ -190,14 +192,14 @@ func Md5fn(filename string) string {
return Md5hex(buf) return Md5hex(buf)
} }
// Return md5 string for "buf" // Md5hex returns an md5 string for "buf"
func Md5hex(buf []byte) string { func Md5hex(buf []byte) string {
rawHash := md5.Sum(buf) rawHash := md5.Sum(buf)
hash := hex.EncodeToString(rawHash[:]) hash := hex.EncodeToString(rawHash[:])
return hash return hash
} }
// Verify that the file size equals "want". This checks: // VerifySize checks that the file size equals "want". This checks:
// 1) Size reported by Stat() // 1) Size reported by Stat()
// 2) Number of bytes returned when reading the whole file // 2) Number of bytes returned when reading the whole file
func VerifySize(t *testing.T, path string, want int) { func VerifySize(t *testing.T, path string, want int) {
@ -216,7 +218,7 @@ func VerifySize(t *testing.T, path string, want int) {
} }
} }
// Create and delete a directory // TestMkdirRmdir creates and deletes a directory
func TestMkdirRmdir(t *testing.T, plainDir string) { func TestMkdirRmdir(t *testing.T, plainDir string) {
dir := plainDir + "/dir1" dir := plainDir + "/dir1"
err := os.Mkdir(dir, 0777) err := os.Mkdir(dir, 0777)
@ -263,7 +265,7 @@ func TestMkdirRmdir(t *testing.T, plainDir string) {
} }
} }
// Create and rename a file // TestRename creates and renames a file
func TestRename(t *testing.T, plainDir string) { func TestRename(t *testing.T, plainDir string) {
file1 := plainDir + "/rename1" file1 := plainDir + "/rename1"
file2 := plainDir + "/rename2" file2 := plainDir + "/rename2"
@ -278,7 +280,7 @@ func TestRename(t *testing.T, plainDir string) {
syscall.Unlink(file2) syscall.Unlink(file2)
} }
// verifyExistence - check in 3 ways that "path" exists: // VerifyExistence checks in 3 ways that "path" exists:
// stat, open, readdir // stat, open, readdir
func VerifyExistence(path string) bool { func VerifyExistence(path string) bool {
// Check that file can be stated // Check that file can be stated