github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/inotify_event.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fs 16 17 import ( 18 "bytes" 19 "fmt" 20 21 "github.com/SagerNet/gvisor/pkg/context" 22 "github.com/SagerNet/gvisor/pkg/hostarch" 23 "github.com/SagerNet/gvisor/pkg/usermem" 24 ) 25 26 // inotifyEventBaseSize is the base size of linux's struct inotify_event. This 27 // must be a power 2 for rounding below. 28 const inotifyEventBaseSize = 16 29 30 // Event represents a struct inotify_event from linux. 31 // 32 // +stateify savable 33 type Event struct { 34 eventEntry 35 36 wd int32 37 mask uint32 38 cookie uint32 39 40 // len is computed based on the name field is set automatically by 41 // Event.setName. It should be 0 when no name is set; otherwise it is the 42 // length of the name slice. 43 len uint32 44 45 // The name field has special padding requirements and should only be set by 46 // calling Event.setName. 47 name []byte 48 } 49 50 func newEvent(wd int32, name string, events, cookie uint32) *Event { 51 e := &Event{ 52 wd: wd, 53 mask: events, 54 cookie: cookie, 55 } 56 if name != "" { 57 e.setName(name) 58 } 59 return e 60 } 61 62 // paddedBytes converts a go string to a null-terminated c-string, padded with 63 // null bytes to a total size of 'l'. 'l' must be large enough for all the bytes 64 // in the 's' plus at least one null byte. 65 func paddedBytes(s string, l uint32) []byte { 66 if l < uint32(len(s)+1) { 67 panic("Converting string to byte array results in truncation, this can lead to buffer-overflow due to the missing null-byte!") 68 } 69 b := make([]byte, l) 70 copy(b, s) 71 72 // b was zero-value initialized during make(), so the rest of the slice is 73 // already filled with null bytes. 74 75 return b 76 } 77 78 // setName sets the optional name for this event. 79 func (e *Event) setName(name string) { 80 // We need to pad the name such that the entire event length ends up a 81 // multiple of inotifyEventBaseSize. 82 unpaddedLen := len(name) + 1 83 // Round up to nearest multiple of inotifyEventBaseSize. 84 e.len = uint32((unpaddedLen + inotifyEventBaseSize - 1) & ^(inotifyEventBaseSize - 1)) 85 // Make sure we haven't overflowed and wrapped around when rounding. 86 if unpaddedLen > int(e.len) { 87 panic("Overflow when rounding inotify event size, the 'name' field was too big.") 88 } 89 e.name = paddedBytes(name, e.len) 90 } 91 92 func (e *Event) sizeOf() int { 93 s := inotifyEventBaseSize + int(e.len) 94 if s < inotifyEventBaseSize { 95 panic("overflow") 96 } 97 return s 98 } 99 100 // CopyTo serializes this event to dst. buf is used as a scratch buffer to 101 // construct the output. We use a buffer allocated ahead of time for 102 // performance. buf must be at least inotifyEventBaseSize bytes. 103 func (e *Event) CopyTo(ctx context.Context, buf []byte, dst usermem.IOSequence) (int64, error) { 104 hostarch.ByteOrder.PutUint32(buf[0:], uint32(e.wd)) 105 hostarch.ByteOrder.PutUint32(buf[4:], e.mask) 106 hostarch.ByteOrder.PutUint32(buf[8:], e.cookie) 107 hostarch.ByteOrder.PutUint32(buf[12:], e.len) 108 109 writeLen := 0 110 111 n, err := dst.CopyOut(ctx, buf) 112 if err != nil { 113 return 0, err 114 } 115 writeLen += n 116 dst = dst.DropFirst(n) 117 118 if e.len > 0 { 119 n, err = dst.CopyOut(ctx, e.name) 120 if err != nil { 121 return 0, err 122 } 123 writeLen += n 124 } 125 126 // Santiy check. 127 if writeLen != e.sizeOf() { 128 panic(fmt.Sprintf("Serialized unexpected amount of data for an event, expected %v, wrote %v.", e.sizeOf(), writeLen)) 129 } 130 131 return int64(writeLen), nil 132 } 133 134 func (e *Event) equals(other *Event) bool { 135 return e.wd == other.wd && 136 e.mask == other.mask && 137 e.cookie == other.cookie && 138 e.len == other.len && 139 bytes.Equal(e.name, other.name) 140 }