~nilium/mtar

6f36c4f62f563067b4464a845fef259f7606599e — Noel Cower 6 years ago 4983050
Adjust how owner/uid/group/gid function

They'll now look up existing entities on the system to use for
names/IDs, rather than taking arbitrary values. This is less flexible
than before, but makes it easier to construct tar files, especially
since you can now do owner=root and get the uid set by doing so.

In addition, when setting owner=NAME or uid=ID, if no group/gid is
provided, the owner's primary group will be used.

Change-Id: I38479d107238b17a92c819d3ca5032826f765269
1 files changed, 63 insertions(+), 49 deletions(-)

M mtar.go
M mtar.go => mtar.go +63 -49
@@ 344,7 344,7 @@ func addFile(w *tar.Writer, src, dest string, opts *FileOpts, allowRecursive boo
		Format:   tar.FormatPAX,
	}

	if uid, gid, ok := getUidGid(st); ok {
	if uid, gid, ok := opts.getUidGid(st); ok {
		hdr.Uid, err = strconv.Atoi(uid.Uid)
		hdr.Uname = uid.Username
		if err != nil {


@@ 479,36 479,16 @@ func shouldSkip(set []Matcher, s string) bool {
	return false
}

func getUidGid(f os.FileInfo) (*user.User, *user.Group, bool) {
	stat, ok := f.Sys().(*syscall.Stat_t)
	if !ok {
		return nil, nil, false
	}
	uid, gid := strconv.FormatUint(uint64(stat.Uid), 10), strconv.FormatUint(uint64(stat.Gid), 10)
	u, err := user.LookupId(uid)
	if err != nil {
		return nil, nil, false
	}
	g, err := user.LookupGroupId(gid)
	if err != nil {
		return nil, nil, false
	}
	return u, g, true
}

type FileOpts struct {
	noRecursive bool

	user  *user.User
	group *user.Group

	// exclusive:
	dir  bool
	link string

	uid      *int
	username string

	gid   *int
	group string

	mode int64

	mtime time.Time


@@ 555,24 535,28 @@ func parseFileOptions(opts string) (*FileOpts, error) {
				return nil, errors.New("may not set an empty link name")
			}
		case strings.HasPrefix(f, "uid="):
			if uid, err := strconv.Atoi(f[len("uid="):]); err != nil {
				return nil, fmt.Errorf("invalid uid: %v", err)
			} else {
				fo.uid = &uid
			uid := f[len("uid="):]
			fo.user, err = user.LookupId(uid)
			if err != nil {
				return nil, fmt.Errorf("unable to lookup group by id %q: %v", uid, err)
			}
		case strings.HasPrefix(f, "gid="):
			if gid, err := strconv.Atoi(f[len("gid="):]); err != nil {
				return nil, fmt.Errorf("invalid gid: %v", err)
			} else {
				fo.gid = &gid
			gid := f[len("gid="):]
			fo.group, err = user.LookupGroupId(gid)
			if err != nil {
				return nil, fmt.Errorf("unable to lookup group by id %q: %v", gid, err)
			}
		case strings.HasPrefix(f, "owner="):
			if fo.username = f[len("owner="):]; fo.username == "" {
				return nil, errors.New("may not set an empty username for an owner")
			owner := f[len("owner="):]
			fo.user, err = user.Lookup(owner)
			if err != nil {
				return nil, fmt.Errorf("unable to lookup owner by name %q: %v", owner, err)
			}
		case strings.HasPrefix(f, "group="):
			if fo.group = f[len("group="):]; fo.group == "" {
				return nil, errors.New("may not set an empty group name")
			group := f[len("group="):]
			fo.group, err = user.LookupGroup(group)
			if err != nil {
				return nil, fmt.Errorf("unable to lookup group by name %q: %v", group, err)
			}
		case strings.HasPrefix(f, "mode="):
			if fo.mode, err = strconv.ParseInt(f[len("mode="):], 0, 64); err != nil {


@@ 633,30 617,60 @@ func parseFileOptions(opts string) (*FileOpts, error) {
		}
	}

	if fo.user != nil && fo.group == nil {
		fo.group, err = user.LookupGroupId(fo.user.Gid)
		if err != nil {
			return nil, fmt.Errorf("unable to look up group for uid %q: %v", fo.user.Uid, err)
		}
	}

	return fo, nil
}

func (f *FileOpts) allowRecursive() bool {
	return f == nil || !f.noRecursive
}
func (f *FileOpts) getUidGid(fi os.FileInfo) (userent *user.User, groupent *user.Group, ok bool) {
	ok = true
	if f != nil {
		userent = f.user
		groupent = f.group
	}

func (f *FileOpts) setHeaderFields(hdr *tar.Header) {
	if f == nil {
	if userent != nil && groupent != nil {
		return
	}

	if f.uid != nil {
		hdr.Uid = *f.uid
	stat, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return nil, nil, false
	}
	if f.gid != nil {
		hdr.Gid = *f.gid

	uid, gid := strconv.FormatUint(uint64(stat.Uid), 10), strconv.FormatUint(uint64(stat.Gid), 10)

	if userent == nil {
		u, err := user.LookupId(uid)
		if err != nil {
			return nil, nil, false
		}
		userent = u
	}

	if f.username != "" {
		hdr.Uname = f.username
	if groupent == nil {
		g, err := user.LookupGroupId(gid)
		if err != nil {
			return nil, nil, false
		}
		groupent = g
	}
	if f.group != "" {
		hdr.Gname = f.group

	return
}

func (f *FileOpts) allowRecursive() bool {
	return f == nil || !f.noRecursive
}

func (f *FileOpts) setHeaderFields(hdr *tar.Header) {
	if f == nil {
		return
	}

	if f.mode != 0 {