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

image/manifest: Recursively remove pre-existing entries when unpacking #42

Merged
merged 2 commits into from
Oct 19, 2017

Conversation

wking
Copy link
Contributor

@wking wking commented Oct 18, 2016

Implementing the logic that is in-flight with opencontainers/image-spec#317, but using recursive removal. GNU tar has a --recursive-unlink option that's not enabled by default, with the motivation being something like “folks would be mad if we blew away a full tree and replaced it with a broken symlink”. That makes sense for working filesystems, but we're building the rootfs from scratch here so losing information is not a concern. This commit always uses recursive removal to get that old thing off the filesystem (whatever it takes ;).

The exception to the removal is if both the tar entry and existing path occupant are directories. In this case we want to use GNU tar's default --overwrite-dir behavior, but unpackLayer's metadata handling is currently very weak (the in-flight #3 strengthens it a bit) so I've left it at “don't delete the old directory”.

The reworked directory case also fixes a minor bug from 44210d0 (opencontainers/image-spec#177) where the:

if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {

block would not error out if the Lstat failed for a reason besides the acceptable IsNotExist. Instead, it would attempt to call MkdirAll, which would probably fail for the same reason that Lstat failed (ENOTDIR?). But it's better to handle the Lstat errors directly.

Fixes #41.

Implementing the logic that is in-flight with [1], but using recursive
removal [2].  GNU tar has a --recursive-unlink option that's not
enabled by default, with the motivation being something like "folks
would be mad if we blew away a full tree and replaced it with a broken
symlink" [3].  That makes sense for working filesystems, but we're
building the rootfs from scratch here so losing information is not a
concern.  This commit always uses recursive removal to get that old
thing off the filesystem (whatever it takes ;).

The exception to the removal is if both the tar entry and existing
path occupant are directories.  In this case we want to use GNU tar's
default --overwrite-dir behavior, but unpackLayer's metadata handling
is currently very weak so I've left it at "don't delete the old
directory".

The reworked directory case also fixes a minor bug from 44210d0
(cmd/oci-image-tool: fix unpacking..., 2016-07-22, opencontainers#177) where the:

  if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {

block would not error out if the Lstat failed for a reason besides the
acceptable IsNotExist.  Instead, it would attempt to call MkdirAll,
which would probably fail for the same reason that Lstat failed
(e.g. ENOTDIR).  But it's better to handle the Lstat errors directly.

[1]: opencontainers/image-spec#317
[2]: https://github.com/opencontainers/image-spec/pull/317/files#r79214718
[3]: https://www.gnu.org/software/tar/manual/html_node/Dealing-with-Old-Files.html

Signed-off-by: W. Trevor King <wking@tremily.us>
To help address:

  $ make lint
  checking lint
  image/manifest.go:140::warning: cyclomatic complexity 39 of function unpackLayer() is high (> 35) (gocyclo)
  ...

Signed-off-by: W. Trevor King <wking@tremily.us>
@wking
Copy link
Contributor Author

wking commented Oct 21, 2016

I've pushed an additional bb40ed1 to address the cyclomatic complexity warning, and Travis is happy now.

}
err = os.MkdirAll(path, info.Mode())
if err != nil {
return err
}
Copy link
Contributor

@xiekeyang xiekeyang Oct 25, 2016

Choose a reason for hiding this comment

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

A debatable view: if it is easier to understand to use like below?

fi, err := os.Lstat(path)
if err==nil{
   ...
}else if  os.IsNotExist(err){
  ...
}else if err!=nil
  ...
}

And, if fi, err := os.Lstat(path) can be replaced to upper lines, like

fi, err := os.Lstat(path)
if err == nil && !fi.IsDir() {
  remove path
}else if !os.IsNotExist(err){
  return err 
}   

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think both of your proposals will end up duplicating the MkdirAll call. And idiomatic Go seems to be “check for and exit on unrecoverable errors as quickly as possible” (which is how I handle it now with the if err != nil && !os.IsNotExist(err)). But I don't really care. If the maintainers feel that avoiding !s in the conditionals is worth pushing the unrecoverable error to the end of the case block, I can reroll to do so.

@zhouhao3
Copy link

zhouhao3 commented Oct 12, 2017

LGTM

Approved with PullApprove

@zhouhao3
Copy link

cc @coolljt0725 @xiekeyang

@zhouhao3
Copy link

ping @coolljt0725 @xiekeyang

@coolljt0725
Copy link
Member

coolljt0725 commented Oct 18, 2017

LGTM ping @opencontainers/image-tools-maintainers

Approved with PullApprove

@coolljt0725 coolljt0725 merged commit f790629 into opencontainers:master Oct 19, 2017
@wking wking deleted the remove-before-unpacking branch October 19, 2017 05:09
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.

oci-image-tool: error extracting layer: symlink /lib/systemd/systemd tmp2/bin/systemd: file exists
5 participants