github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/oci/casext/map.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016, 2017, 2018 SUSE LLC. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package casext 19 20 import ( 21 "reflect" 22 23 "github.com/apex/log" 24 ispec "github.com/opencontainers/image-spec/specs-go/v1" 25 "github.com/pkg/errors" 26 ) 27 28 // Used by walkState.mark() to determine which struct members are descriptors to 29 // recurse into them. We aren't interested in struct members which are not 30 // either a slice of ispec.Descriptor or ispec.Descriptor themselves. 31 var descriptorType = reflect.TypeOf(ispec.Descriptor{}) 32 33 // DescriptorMapFunc is a function that is used to provide a mapping between 34 // different descriptor values with MapDescriptors. It will not be called 35 // concurrently, and will only be called once for each recursively resolved 36 // element. 37 type DescriptorMapFunc func(ispec.Descriptor) ispec.Descriptor 38 39 // isDescriptor returns whether the given T is a ispec.Descriptor. 40 func isDescriptor(T reflect.Type) bool { 41 return T == descriptorType 42 } 43 44 func mapDescriptors(V reflect.Value, mapFunc DescriptorMapFunc) error { 45 // We can ignore this value. 46 if !V.IsValid() { 47 return nil 48 } 49 50 // First check that V isn't actually a ispec.Descriptor, if it is then 51 // we're done. 52 if isDescriptor(V.Type()) { 53 old := V.Interface().(ispec.Descriptor) 54 new := mapFunc(old) 55 56 // We only need to do any assignment if the two are not equal. 57 if !reflect.DeepEqual(new, old) { 58 // P is a ptr to V (or just V if it's already a pointer). 59 P := V 60 if !P.CanSet() { 61 // This is a programmer error. 62 return errors.Errorf("[internal error] cannot apply map function to %v: %v is not settable!", P, P.Type()) 63 } 64 P.Set(reflect.ValueOf(new)) 65 } 66 return nil 67 } 68 69 // Recurse into all the types. 70 switch V.Kind() { 71 case reflect.Ptr, reflect.Interface: 72 // Just deref the pointer/interface. 73 if V.IsNil() { 74 return nil 75 } 76 err := mapDescriptors(V.Elem(), mapFunc) 77 return errors.Wrapf(err, "%v", V.Type()) 78 79 case reflect.Slice, reflect.Array: 80 // Iterate over each element. 81 for idx := 0; idx < V.Len(); idx++ { 82 err := mapDescriptors(V.Index(idx), mapFunc) 83 if err != nil { 84 return errors.Wrapf(err, "%v[%d]->%v", V.Type(), idx, V.Index(idx).Type()) 85 } 86 } 87 return nil 88 89 case reflect.Struct: 90 // We are only ever going to be interested in ispec.* types. 91 // XXX: This is something we might want to revisit in the future. 92 if V.Type().PkgPath() != descriptorType.PkgPath() { 93 log.WithFields(log.Fields{ 94 "name": V.Type().PkgPath() + "::" + V.Type().Name(), 95 "v1path": descriptorType.PkgPath(), 96 }).Debugf("detected escape to outside ispec.* namespace") 97 return nil 98 } 99 100 // We can now actually iterate through a struct to find all descriptors. 101 for idx := 0; idx < V.NumField(); idx++ { 102 err := mapDescriptors(V.Field(idx), mapFunc) 103 if err != nil { 104 return errors.Wrapf(err, "%v[%d=%s]->%v", V.Type(), idx, V.Type().Field(idx).Name, V.Field(idx).Type()) 105 } 106 } 107 return nil 108 109 default: 110 // FIXME: Should we log something here? While this will be hit normally 111 // (namely when we hit an io.ReadCloser) this seems a bit 112 // careless. 113 return nil 114 } 115 116 // Unreachable. 117 } 118 119 // MapDescriptors applies the given function once for every instance of 120 // ispec.Descriptor found in the given type, and replaces it with the returned 121 // value (which may be the same). This is done through the reflection API in 122 // Go, which means that hidden attributes may be inaccessible. 123 // DescriptorMapFunc will only be executed once for every ispec.Descriptor 124 // found. 125 func MapDescriptors(i interface{}, mapFunc DescriptorMapFunc) error { 126 return mapDescriptors(reflect.ValueOf(i), mapFunc) 127 }