github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/fsutil/fsutil_unsafe.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 fsutil 16 17 import ( 18 "unsafe" 19 20 "github.com/ttpreport/gvisor-ligolo/pkg/syserr" 21 "golang.org/x/sys/unix" 22 ) 23 24 // UnixDirentMaxSize is the maximum size of unix.Dirent in bytes. 25 var UnixDirentMaxSize = int(unsafe.Sizeof(unix.Dirent{})) 26 27 // Utimensat is a convenience wrapper to make the utimensat(2) syscall. It 28 // additionally handles empty name. 29 func Utimensat(dirFd int, name string, times [2]unix.Timespec, flags int) error { 30 // utimensat(2) doesn't accept empty name, instead name must be nil to make it 31 // operate directly on 'dirFd' unlike other *at syscalls. 32 var namePtr unsafe.Pointer 33 if name != "" { 34 nameBytes, err := unix.BytePtrFromString(name) 35 if err != nil { 36 return err 37 } 38 namePtr = unsafe.Pointer(nameBytes) 39 } 40 41 timesPtr := unsafe.Pointer(×[0]) 42 43 if _, _, errno := unix.Syscall6( 44 unix.SYS_UTIMENSAT, 45 uintptr(dirFd), 46 uintptr(namePtr), 47 uintptr(timesPtr), 48 uintptr(flags), 49 0, 50 0); errno != 0 { 51 52 return syserr.FromHost(errno).ToError() 53 } 54 return nil 55 } 56 57 // RenameAt is a convenience wrapper to make the renameat(2) syscall. It 58 // additionally handles empty names. 59 func RenameAt(oldDirFD int, oldName string, newDirFD int, newName string) error { 60 var oldNamePtr unsafe.Pointer 61 if oldName != "" { 62 nameBytes, err := unix.BytePtrFromString(oldName) 63 if err != nil { 64 return err 65 } 66 oldNamePtr = unsafe.Pointer(nameBytes) 67 } 68 var newNamePtr unsafe.Pointer 69 if newName != "" { 70 nameBytes, err := unix.BytePtrFromString(newName) 71 if err != nil { 72 return err 73 } 74 newNamePtr = unsafe.Pointer(nameBytes) 75 } 76 77 if _, _, errno := unix.Syscall6( 78 unix.SYS_RENAMEAT, 79 uintptr(oldDirFD), 80 uintptr(oldNamePtr), 81 uintptr(newDirFD), 82 uintptr(newNamePtr), 83 0, 84 0); errno != 0 { 85 86 return syserr.FromHost(errno).ToError() 87 } 88 return nil 89 } 90 91 // ParseDirents parses dirents from buf. buf must have been populated by 92 // getdents64(2) syscall. It calls the handleDirent callback for each dirent. 93 func ParseDirents(buf []byte, handleDirent func(ino uint64, off int64, ftype uint8, name string, reclen uint16) bool) { 94 for len(buf) > 0 { 95 // Interpret the buf populated by unix.Getdents as unix.Dirent. 96 dirent := *(*unix.Dirent)(unsafe.Pointer(&buf[0])) 97 98 // Advance buf for the next dirent. 99 buf = buf[dirent.Reclen:] 100 101 // Extracting the name is pretty tedious... 102 var nameBuf [unix.NAME_MAX]byte 103 var nameLen int 104 for i := 0; i < len(dirent.Name); i++ { 105 // The name is null terminated. 106 if dirent.Name[i] == 0 { 107 nameLen = i 108 break 109 } 110 nameBuf[i] = byte(dirent.Name[i]) 111 } 112 name := string(nameBuf[:nameLen]) 113 114 // Skip `.` and `..` entries. It is anyways ignored by the client. We also 115 // don't want to leak information about `..`. 116 if name == "." || name == ".." { 117 continue 118 } 119 120 // Deliver results to caller. 121 if !handleDirent(dirent.Ino, dirent.Off, dirent.Type, name, dirent.Reclen) { 122 return 123 } 124 } 125 }