| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | // +build linux netbsd freebsd darwin
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package copier | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"syscall" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/pkg/errors" | 
					
						
							|  |  |  | 	"golang.org/x/sys/unix" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	xattrsSupported = true | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	relevantAttributes = []string{"security.capability", "security.ima", "user.*"} // the attributes that we preserve - we discard others
 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												copier: add Mkdir()
Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a
directory exists without having to possibly create it directly from
outside of a chroot.
Make use of filepath.ToSlash() and filepath.FromSlash() where it's
appropriate.
Add more unit tests.
Address some review comments:
* Check for ERANGE instead of E2BIG errors from llistxattr() and
  lgetxattr() as indicators that the buffer we passed to them is too
  small.
* Factor an `isRelevantXattr` helper out of Lgetxattrs() and
  Lsetxattrs().
* Drop our getcwd() function in favor of using os.Getwd().
* Adjust the comment describing the GetOptions.KeepDirectoryNames field.
* Clean hdr.Name before attempting to compute where an item being
  extracted should go.
* When writing items to the filesystem, create with 0?00 permissions,
  set ownership, then set the correct permissions.
* Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error
  into Response.Error.
* When reading items from the filesystem, if a glob matches multiple
  items, and one of the items is excluded, continue checking the other
  items.
* Make sure we always Wait() on a child process if we spawned one.
* Clean up the cleanup logic for pipes that we use to communicate with a
  child process.
* Clean up the kill-the-child-process logic we call when we encounter an
  error communicating with the child process.
* Drop the separate Options structure, use helper methods to simplify
  pulling out the right ID maps and exclusions list for the request.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2020-07-31 02:00:25 +08:00
										 |  |  | // isRelevantXattr checks if "attribute" matches one of the attribute patterns
 | 
					
						
							|  |  |  | // listed in the "relevantAttributes" list.
 | 
					
						
							|  |  |  | func isRelevantXattr(attribute string) bool { | 
					
						
							|  |  |  | 	for _, relevant := range relevantAttributes { | 
					
						
							|  |  |  | 		matched, err := filepath.Match(relevant, attribute) | 
					
						
							|  |  |  | 		if err != nil || !matched { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | // Lgetxattrs returns a map of the relevant extended attributes set on the given file.
 | 
					
						
							|  |  |  | func Lgetxattrs(path string) (map[string]string, error) { | 
					
						
							|  |  |  | 	maxSize := 64 * 1024 * 1024 | 
					
						
							|  |  |  | 	listSize := 64 * 1024 | 
					
						
							|  |  |  | 	var list []byte | 
					
						
							|  |  |  | 	for listSize < maxSize { | 
					
						
							|  |  |  | 		list = make([]byte, listSize) | 
					
						
							|  |  |  | 		size, err := unix.Llistxattr(path, list) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
											  
											
												copier: add Mkdir()
Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a
directory exists without having to possibly create it directly from
outside of a chroot.
Make use of filepath.ToSlash() and filepath.FromSlash() where it's
appropriate.
Add more unit tests.
Address some review comments:
* Check for ERANGE instead of E2BIG errors from llistxattr() and
  lgetxattr() as indicators that the buffer we passed to them is too
  small.
* Factor an `isRelevantXattr` helper out of Lgetxattrs() and
  Lsetxattrs().
* Drop our getcwd() function in favor of using os.Getwd().
* Adjust the comment describing the GetOptions.KeepDirectoryNames field.
* Clean hdr.Name before attempting to compute where an item being
  extracted should go.
* When writing items to the filesystem, create with 0?00 permissions,
  set ownership, then set the correct permissions.
* Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error
  into Response.Error.
* When reading items from the filesystem, if a glob matches multiple
  items, and one of the items is excluded, continue checking the other
  items.
* Make sure we always Wait() on a child process if we spawned one.
* Clean up the cleanup logic for pipes that we use to communicate with a
  child process.
* Clean up the kill-the-child-process logic we call when we encounter an
  error communicating with the child process.
* Drop the separate Options structure, use helper methods to simplify
  pulling out the right ID maps and exclusions list for the request.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2020-07-31 02:00:25 +08:00
										 |  |  | 			if unwrapError(err) == syscall.ERANGE { | 
					
						
							| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | 				listSize *= 2 | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error listing extended attributes of %q", path) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		list = list[:size] | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if listSize >= maxSize { | 
					
						
							|  |  |  | 		return nil, errors.Errorf("unable to read list of attributes for %q: size would have been too big", path) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	m := make(map[string]string) | 
					
						
							|  |  |  | 	for _, attribute := range strings.Split(string(list), string('\000')) { | 
					
						
							| 
									
										
											  
											
												copier: add Mkdir()
Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a
directory exists without having to possibly create it directly from
outside of a chroot.
Make use of filepath.ToSlash() and filepath.FromSlash() where it's
appropriate.
Add more unit tests.
Address some review comments:
* Check for ERANGE instead of E2BIG errors from llistxattr() and
  lgetxattr() as indicators that the buffer we passed to them is too
  small.
* Factor an `isRelevantXattr` helper out of Lgetxattrs() and
  Lsetxattrs().
* Drop our getcwd() function in favor of using os.Getwd().
* Adjust the comment describing the GetOptions.KeepDirectoryNames field.
* Clean hdr.Name before attempting to compute where an item being
  extracted should go.
* When writing items to the filesystem, create with 0?00 permissions,
  set ownership, then set the correct permissions.
* Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error
  into Response.Error.
* When reading items from the filesystem, if a glob matches multiple
  items, and one of the items is excluded, continue checking the other
  items.
* Make sure we always Wait() on a child process if we spawned one.
* Clean up the cleanup logic for pipes that we use to communicate with a
  child process.
* Clean up the kill-the-child-process logic we call when we encounter an
  error communicating with the child process.
* Drop the separate Options structure, use helper methods to simplify
  pulling out the right ID maps and exclusions list for the request.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2020-07-31 02:00:25 +08:00
										 |  |  | 		if isRelevantXattr(attribute) { | 
					
						
							| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | 			attributeSize := 64 * 1024 | 
					
						
							|  |  |  | 			var attributeValue []byte | 
					
						
							|  |  |  | 			for attributeSize < maxSize { | 
					
						
							|  |  |  | 				attributeValue = make([]byte, attributeSize) | 
					
						
							|  |  |  | 				size, err := unix.Lgetxattr(path, attribute, attributeValue) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
											  
											
												copier: add Mkdir()
Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a
directory exists without having to possibly create it directly from
outside of a chroot.
Make use of filepath.ToSlash() and filepath.FromSlash() where it's
appropriate.
Add more unit tests.
Address some review comments:
* Check for ERANGE instead of E2BIG errors from llistxattr() and
  lgetxattr() as indicators that the buffer we passed to them is too
  small.
* Factor an `isRelevantXattr` helper out of Lgetxattrs() and
  Lsetxattrs().
* Drop our getcwd() function in favor of using os.Getwd().
* Adjust the comment describing the GetOptions.KeepDirectoryNames field.
* Clean hdr.Name before attempting to compute where an item being
  extracted should go.
* When writing items to the filesystem, create with 0?00 permissions,
  set ownership, then set the correct permissions.
* Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error
  into Response.Error.
* When reading items from the filesystem, if a glob matches multiple
  items, and one of the items is excluded, continue checking the other
  items.
* Make sure we always Wait() on a child process if we spawned one.
* Clean up the cleanup logic for pipes that we use to communicate with a
  child process.
* Clean up the kill-the-child-process logic we call when we encounter an
  error communicating with the child process.
* Drop the separate Options structure, use helper methods to simplify
  pulling out the right ID maps and exclusions list for the request.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2020-07-31 02:00:25 +08:00
										 |  |  | 					if unwrapError(err) == syscall.ERANGE { | 
					
						
							| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | 						attributeSize *= 2 | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return nil, errors.Wrapf(err, "error getting value of extended attribute %q on %q", attribute, path) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				m[attribute] = string(attributeValue[:size]) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if attributeSize >= maxSize { | 
					
						
							|  |  |  | 				return nil, errors.Errorf("unable to read attribute %q of %q: size would have been too big", attribute, path) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return m, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lsetxattrs sets the relevant members of the specified extended attributes on the given file.
 | 
					
						
							|  |  |  | func Lsetxattrs(path string, xattrs map[string]string) error { | 
					
						
							|  |  |  | 	for attribute, value := range xattrs { | 
					
						
							| 
									
										
											  
											
												copier: add Mkdir()
Add a function for doing Mkdir-possibly-in-a-chroot, for ensuring that a
directory exists without having to possibly create it directly from
outside of a chroot.
Make use of filepath.ToSlash() and filepath.FromSlash() where it's
appropriate.
Add more unit tests.
Address some review comments:
* Check for ERANGE instead of E2BIG errors from llistxattr() and
  lgetxattr() as indicators that the buffer we passed to them is too
  small.
* Factor an `isRelevantXattr` helper out of Lgetxattrs() and
  Lsetxattrs().
* Drop our getcwd() function in favor of using os.Getwd().
* Adjust the comment describing the GetOptions.KeepDirectoryNames field.
* Clean hdr.Name before attempting to compute where an item being
  extracted should go.
* When writing items to the filesystem, create with 0?00 permissions,
  set ownership, then set the correct permissions.
* Merge StatResponse.Error, GetResponse.Error, and PutResponse.Error
  into Response.Error.
* When reading items from the filesystem, if a glob matches multiple
  items, and one of the items is excluded, continue checking the other
  items.
* Make sure we always Wait() on a child process if we spawned one.
* Clean up the cleanup logic for pipes that we use to communicate with a
  child process.
* Clean up the kill-the-child-process logic we call when we encounter an
  error communicating with the child process.
* Drop the separate Options structure, use helper methods to simplify
  pulling out the right ID maps and exclusions list for the request.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2020-07-31 02:00:25 +08:00
										 |  |  | 		if isRelevantXattr(attribute) { | 
					
						
							|  |  |  | 			if err := unix.Lsetxattr(path, attribute, []byte(value), 0); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-15 04:34:07 +08:00
										 |  |  | 				return errors.Wrapf(err, "error setting value of extended attribute %q on %q", attribute, path) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |