1
0
Fork 0
mirror of https://github.com/vbatts/go-mtree.git synced 2025-07-01 05:28:30 +00:00

*: update -u behavior

Fixes #16

In attempt to close https://github.com/vbatts/go-mtree/issues/16 I've
uncovered that the update was missing a function for symlink.
Additionally the update was not even opperating on the correct directory
hierarchy.

I've uncovered that os.Chtimes follows the symlink, and presumably only
Linux has an obscure way to set the mtime/atime on a symlink itself. So
I've made a custom lchtimes().

Also Mode follows through the symlink, and symlinks only ever have a
mode of 0777, so don't set them.

Lastly, directories need to have their mtime/atime set in a reverse
order after all other updates have been done. This is going to require
something like a `container/heap` to be unwound.

Also, each updateFunc will _only_ perform the update if it is needed. Much less
invasive this way.

Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
Vincent Batts 2017-06-26 15:12:31 -04:00
parent a393e171c4
commit 73be830998
Signed by: vbatts
GPG key ID: 10937E57733F1362
9 changed files with 304 additions and 18 deletions

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/vbatts/go-mtree/pkg/govis"
)
// UpdateKeywordFunc is the signature for a function that will restore a file's
@ -24,6 +25,7 @@ var UpdateKeywordFuncs = map[Keyword]UpdateKeywordFunc{
"uid": uidUpdateKeywordFunc,
"gid": gidUpdateKeywordFunc,
"xattr": xattrUpdateKeywordFunc,
"link": linkUpdateKeywordFunc,
}
func uidUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
@ -31,6 +33,15 @@ func uidUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
if err != nil {
return nil, err
}
stat, err := os.Lstat(path)
if err != nil {
return nil, err
}
if statIsUID(stat, uid) {
return stat, nil
}
if err := os.Lchown(path, uid, -1); err != nil {
return nil, err
}
@ -42,6 +53,15 @@ func gidUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
if err != nil {
return nil, err
}
stat, err := os.Lstat(path)
if err != nil {
return nil, err
}
if statIsGID(stat, gid) {
return stat, nil
}
if err := os.Lchown(path, -1, gid); err != nil {
return nil, err
}
@ -49,10 +69,28 @@ func gidUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
}
func modeUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
info, err := os.Lstat(path)
if err != nil {
return nil, err
}
// don't set mode on symlinks, as it passes through to the backing file
if info.Mode()&os.ModeSymlink != 0 {
return info, nil
}
vmode, err := strconv.ParseInt(kv.Value(), 8, 32)
if err != nil {
return nil, err
}
stat, err := os.Lstat(path)
if err != nil {
return nil, err
}
if stat.Mode() == os.FileMode(vmode) {
return stat, nil
}
logrus.Debugf("path: %q, kv.Value(): %q, vmode: %o", path, kv.Value(), vmode)
if err := os.Chmod(path, os.FileMode(vmode)); err != nil {
return nil, err
@ -85,7 +123,19 @@ func tartimeUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
}
vtime := time.Unix(sec, 0)
if err := os.Chtimes(path, vtime, vtime); err != nil {
// if times are same then don't modify anything
// comparing Unix, since it does not include Nano seconds
if info.ModTime().Unix() == vtime.Unix() {
return info, nil
}
// symlinks are strange and most of the time passes through to the backing file
if info.Mode()&os.ModeSymlink != 0 {
if err := lchtimes(path, vtime, vtime); err != nil {
return nil, err
}
} else if err := os.Chtimes(path, vtime, vtime); err != nil {
return nil, err
}
return os.Lstat(path)
@ -93,6 +143,11 @@ func tartimeUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
// this is nano second precision
func timeUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
info, err := os.Lstat(path)
if err != nil {
return nil, err
}
v := strings.SplitN(kv.Value(), ".", 2)
if len(v) != 2 {
return nil, fmt.Errorf("expected a number like 1469104727.871937272")
@ -101,11 +156,46 @@ func timeUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
if err != nil {
return nil, fmt.Errorf("expected nano seconds, but got %q", v[0]+v[1])
}
logrus.Debugf("arg: %q; nsec: %q", v[0]+v[1], nsec)
logrus.Debugf("arg: %q; nsec: %d", v[0]+v[1], nsec)
vtime := time.Unix(0, nsec)
if err := os.Chtimes(path, vtime, vtime); err != nil {
// if times are same then don't modify anything
if info.ModTime().Equal(vtime) {
return info, nil
}
// symlinks are strange and most of the time passes through to the backing file
if info.Mode()&os.ModeSymlink != 0 {
if err := lchtimes(path, vtime, vtime); err != nil {
return nil, err
}
} else if err := os.Chtimes(path, vtime, vtime); err != nil {
return nil, err
}
return os.Lstat(path)
}
func linkUpdateKeywordFunc(path string, kv KeyVal) (os.FileInfo, error) {
linkname, err := govis.Unvis(kv.Value(), DefaultVisFlags)
if err != nil {
return nil, err
}
got, err := os.Readlink(path)
if err != nil {
return nil, err
}
if got == linkname {
return os.Lstat(path)
}
logrus.Debugf("linkUpdateKeywordFunc: removing %q to link to %q", path, linkname)
if err := os.Remove(path); err != nil {
return nil, err
}
if err := os.Symlink(linkname, path); err != nil {
return nil, err
}
return os.Lstat(path)
}