github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/oci/casext/map.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 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/opencontainers/umoci/oci/casext/mediatype" 26 "github.com/pkg/errors" 27 ) 28 29 // Used by walkState.mark() to determine which struct members are descriptors to 30 // recurse into them. We aren't interested in struct members which are not 31 // either a slice of ispec.Descriptor or ispec.Descriptor themselves. 32 var descriptorType = reflect.TypeOf(ispec.Descriptor{}) 33 34 // DescriptorMapFunc is a function that is used to provide a mapping between 35 // different descriptor values with MapDescriptors. It will not be called 36 // concurrently, and will only be called once for each recursively resolved 37 // element. 38 type DescriptorMapFunc func(ispec.Descriptor) ispec.Descriptor 39 40 // isDescriptor returns whether the given T is a ispec.Descriptor. 41 func isDescriptor(T reflect.Type) bool { 42 return T == descriptorType 43 } 44 45 func mapDescriptors(V reflect.Value, mapFunc DescriptorMapFunc) error { 46 // We can ignore this value. 47 if !V.IsValid() { 48 return nil 49 } 50 51 // First check that V isn't actually a ispec.Descriptor, if it is then 52 // we're done. 53 if isDescriptor(V.Type()) { 54 old := V.Interface().(ispec.Descriptor) 55 new := mapFunc(old) 56 57 // We only need to do any assignment if the two are not equal. 58 if !reflect.DeepEqual(new, old) { 59 // P is a ptr to V (or just V if it's already a pointer). 60 P := V 61 if !P.CanSet() { 62 // This is a programmer error. 63 return errors.Errorf("[internal error] cannot apply map function to %v: %v is not settable!", P, P.Type()) 64 } 65 P.Set(reflect.ValueOf(new)) 66 } 67 return nil 68 } 69 70 // Recurse into all the types. 71 switch V.Kind() { 72 case reflect.Ptr, reflect.Interface: 73 // Just deref the pointer/interface. 74 if V.IsNil() { 75 return nil 76 } 77 err := mapDescriptors(V.Elem(), mapFunc) 78 return errors.Wrapf(err, "%v", V.Type()) 79 80 case reflect.Slice, reflect.Array: 81 // Iterate over each element. 82 for idx := 0; idx < V.Len(); idx++ { 83 err := mapDescriptors(V.Index(idx), mapFunc) 84 if err != nil { 85 return errors.Wrapf(err, "%v[%d]->%v", V.Type(), idx, V.Index(idx).Type()) 86 } 87 } 88 return nil 89 90 case reflect.Struct: 91 // We are only ever going to be interested in registered types. 92 if !mediatype.IsRegisteredPackage(V.Type().PkgPath()) { 93 log.WithFields(log.Fields{ 94 "name": V.Type().PkgPath() + "::" + V.Type().Name(), 95 "v1path": descriptorType.PkgPath(), 96 }).Debugf("detected jump outside permitted packages") 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 }