Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filesystem: allow .fscrypt to be a symlink #150

Merged
merged 2 commits into from
Oct 24, 2019
Merged

filesystem: allow .fscrypt to be a symlink #150

merged 2 commits into from
Oct 24, 2019

Conversation

ebiggers
Copy link
Collaborator

Support the case where the user has a read-only root filesystem (e.g.
with OSTree) and had previously created a symlink /.fscrypt pointing to
a writable location, so that login protectors can be created there.

Resolves #131

Copy link
Member

@josephlr josephlr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing the issues and for adding the tests!!

@@ -129,6 +133,23 @@ func (m *Mount) BaseDir() string {
return filepath.Join(m.Path, baseDirName)
}

// baseDirReal is like BaseDir, except it evaluates the symlink if the BaseDir
// is a symlink.
func (m *Mount) baseDirReal() (string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this approch will work, but it seems like it would be simpler to just have BaseDir call filepath.EvalSymlinks and just return that for all BaseDir operations. That would also help keep the symlink code size down and in one place.

// BaseDir returns the path of the base fscrypt directory on this filesystem after resolving symlinks.
func (m *Mount) BaseDir() string {
	dir, err := filepath.EvalSymlinks(filepath.Join(m.Path, baseDirName))
        if err != nil {
                panic(err)
        }
        return dir
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't work because EvalSymlinks returns an error if the symlink target doesn't exist. So I went with:

func (m *Mount) BaseDir() string {
        rawBaseDir := filepath.Join(m.Path, baseDirName)
        target, err := os.Readlink(rawBaseDir)
        if err != nil {
                return rawBaseDir // not a symlink
        }
        if filepath.IsAbs(target) {
                return target
        }
        return filepath.Join(m.Path, target)
}

One thing I don't quite like about either approach is that it causes BaseDir() (which is called several times on each mount) to do syscalls rather than just return a string. The single readlink() isn't too bad in comparison to the full path walk done by EvalSymlinks() though, so I suppose that for simplicity in the code we should just go with that for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much easier for me to understand. If it turns out BaseDir() making syscalls is a bottleneck for the fscrypt commands, we can always just cache the result (so that readLink is only ever called once per mount).

filesystem/filesystem.go Outdated Show resolved Hide resolved
@@ -56,6 +56,16 @@ func loggedStat(name string) (os.FileInfo, error) {
return info, err
}

// loggedLstat runs os.Lstat (doesn't dereference trailing symlink), but it logs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file would then be unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still useful to have isSymlink() for the test, so I've kept it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As these are test only I'm just going to move them into the test file (just to make it clear what code is test-only).

filesystem/filesystem_test.go Outdated Show resolved Hide resolved
Support the case where the user has a read-only root filesystem (e.g.
with OSTree) and had previously created a symlink /.fscrypt pointing to
a writable location, so that login protectors can be created there.

Resolves #131
@ebiggers
Copy link
Collaborator Author

@josephlr, any more comments on this pull request?

Copy link
Member

@josephlr josephlr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!

@@ -56,6 +56,16 @@ func loggedStat(name string) (os.FileInfo, error) {
return info, err
}

// loggedLstat runs os.Lstat (doesn't dereference trailing symlink), but it logs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As these are test only I'm just going to move them into the test file (just to make it clear what code is test-only).

This makes it easier to understand which code is actually invoked by the
command-line tool.
@josephlr josephlr merged commit f819c93 into google:master Oct 24, 2019
@ebiggers ebiggers deleted the allow-metadata-symlink branch October 24, 2019 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

compatibility between ostree and fscrypt
2 participants