Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for Cwd() on Windows #1163

Merged
merged 1 commit into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,22 @@ func Test_IsRunning(t *testing.T) {
}
}

func Test_Process_Cwd(t *testing.T) {
myPid := os.Getpid()
currentWorkingDirectory, _ := os.Getwd()

process, _ := NewProcess(int32(myPid))
pidCwd, err := process.Cwd()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Fatalf("getting cwd error %v", err)
}
pidCwd = strings.TrimSuffix(pidCwd, string(os.PathSeparator))
assert.Equal(t, currentWorkingDirectory, pidCwd)

t.Log(pidCwd)
}

func Test_Process_Environ(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
Expand Down Expand Up @@ -811,6 +827,23 @@ func Test_AllProcesses_environ(t *testing.T) {
}
}

func Test_AllProcesses_Cwd(t *testing.T) {
procs, err := Processes()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Fatalf("getting processes error %v", err)
}
for _, proc := range procs {
exeName, _ := proc.Exe()
cwd, err := proc.Cwd()
if err != nil {
cwd = "Error: " + err.Error()
}

t.Logf("Process #%v: Name: %v / Current Working Directory: %s\n", proc.Pid, exeName, cwd)
}
}

func BenchmarkNewProcess(b *testing.B) {
checkPid := os.Getpid()
for i := 0; i < b.N; i++ {
Expand Down
104 changes: 84 additions & 20 deletions process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,30 +137,54 @@ type processEnvironmentBlock64 struct {
}

type rtlUserProcessParameters32 struct {
Reserved1 [16]uint8
Reserved2 [10]uint32
Reserved1 [16]uint8
ConsoleHandle uint32
ConsoleFlags uint32
StdInputHandle uint32
StdOutputHandle uint32
StdErrorHandle uint32
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
CurrentDirectoryPathAddress uint32
CurrentDirectoryHandle uint32
DllPathNameLength uint16
_ uint16 // Max Length
DllPathAddress uint32
ImagePathNameLength uint16
_ uint16
ImagePathAddress uint32
CommandLineLength uint16
_ uint16
CommandLineAddress uint32
EnvironmentAddress uint32
_ uint16 // Max Length
ImagePathAddress uint32
CommandLineLength uint16
_ uint16 // Max Length
CommandLineAddress uint32
EnvironmentAddress uint32
// More fields which we don't use so far
}

type rtlUserProcessParameters64 struct {
Reserved1 [16]uint8
Reserved2 [10]uint64
Reserved1 [16]uint8
ConsoleHandle uint64
ConsoleFlags uint64
StdInputHandle uint64
StdOutputHandle uint64
StdErrorHandle uint64
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CurrentDirectoryPathAddress uint64
CurrentDirectoryHandle uint64
DllPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
DllPathAddress uint64
ImagePathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
ImagePathAddress uint64
CommandLineLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CommandLineAddress uint64
EnvironmentAddress uint64
_ uint16 // Max Length
_ uint32 // Padding
ImagePathAddress uint64
CommandLineLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CommandLineAddress uint64
EnvironmentAddress uint64
// More fields which we don't use so far
}

Expand Down Expand Up @@ -377,8 +401,48 @@ func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
return ru.CreationTime.Nanoseconds() / 1000000, nil
}

func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
func (p *Process) CwdWithContext(_ context.Context) (string, error) {
h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid))
if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
return "", nil
}
if err != nil {
return "", err
}
defer syscall.CloseHandle(syscall.Handle(h))

procIs32Bits := is32BitProcess(h)

if procIs32Bits {
userProcParams, err := getUserProcessParams32(h)
if err != nil {
return "", err
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathAddress) {
return "", errors.New("cannot read current working directory")
}

return convertUTF16ToString(cwd), nil
}
} else {
userProcParams, err := getUserProcessParams64(h)
if err != nil {
return "", err
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
return "", errors.New("cannot read current working directory")
}

return convertUTF16ToString(cwd), nil
}
}

//if we reach here, we have no cwd
return "", nil
}

func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
Expand Down
33 changes: 33 additions & 0 deletions v3/process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,22 @@ func Test_Process_Environ(t *testing.T) {
}
}

func Test_Process_Cwd(t *testing.T) {
myPid := os.Getpid()
currentWorkingDirectory, _ := os.Getwd()

process, _ := NewProcess(int32(myPid))
pidCwd, err := process.Cwd()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Fatalf("getting cwd error %v", err)
}
pidCwd = strings.TrimSuffix(pidCwd, string(os.PathSeparator))
assert.Equal(t, currentWorkingDirectory, pidCwd)

t.Log(pidCwd)
}

func Test_AllProcesses_cmdLine(t *testing.T) {
procs, err := Processes()
skipIfNotImplementedErr(t, err)
Expand Down Expand Up @@ -813,6 +829,23 @@ func Test_AllProcesses_environ(t *testing.T) {
}
}

func Test_AllProcesses_Cwd(t *testing.T) {
procs, err := Processes()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Fatalf("getting processes error %v", err)
}
for _, proc := range procs {
exeName, _ := proc.Exe()
cwd, err := proc.Cwd()
if err != nil {
cwd = "Error: " + err.Error()
}

t.Logf("Process #%v: Name: %v / Current Working Directory: %s\n", proc.Pid, exeName, cwd)
}
}

func BenchmarkNewProcess(b *testing.B) {
checkPid := os.Getpid()
for i := 0; i < b.N; i++ {
Expand Down
76 changes: 70 additions & 6 deletions v3/process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,44 @@ type processEnvironmentBlock64 struct {

type rtlUserProcessParameters32 struct {
Reserved1 [16]uint8
Reserved2 [10]uint32
ConsoleHandle uint32
ConsoleFlags uint32
StdInputHandle uint32
StdOutputHandle uint32
StdErrorHandle uint32
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
CurrentDirectoryPathAddress uint32
CurrentDirectoryHandle uint32
DllPathNameLength uint16
_ uint16 // Max Length
DllPathAddress uint32
ImagePathNameLength uint16
_ uint16
_ uint16 // Max Length
ImagePathAddress uint32
CommandLineLength uint16
_ uint16
_ uint16 // Max Length
CommandLineAddress uint32
EnvironmentAddress uint32
// More fields which we don't use so far
}

type rtlUserProcessParameters64 struct {
Reserved1 [16]uint8
Reserved2 [10]uint64
ConsoleHandle uint64
ConsoleFlags uint64
StdInputHandle uint64
StdOutputHandle uint64
StdErrorHandle uint64
CurrentDirectoryPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
CurrentDirectoryPathAddress uint64
CurrentDirectoryHandle uint64
DllPathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
DllPathAddress uint64
ImagePathNameLength uint16
_ uint16 // Max Length
_ uint32 // Padding
Expand Down Expand Up @@ -364,8 +388,48 @@ func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
return ru.CreationTime.Nanoseconds() / 1000000, nil
}

func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
func (p *Process) CwdWithContext(_ context.Context) (string, error) {
h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid))
if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
return "", nil
}
if err != nil {
return "", err
}
defer syscall.CloseHandle(syscall.Handle(h))

procIs32Bits := is32BitProcess(h)

if procIs32Bits {
userProcParams, err := getUserProcessParams32(h)
if err != nil {
return "", err
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathAddress) {
return "", errors.New("cannot read current working directory")
}

return convertUTF16ToString(cwd), nil
}
} else {
userProcParams, err := getUserProcessParams64(h)
if err != nil {
return "", err
}
if userProcParams.CurrentDirectoryPathNameLength > 0 {
cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength))
if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
return "", errors.New("cannot read current working directory")
}

return convertUTF16ToString(cwd), nil
}
}

//if we reach here, we have no cwd
return "", nil
}

func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
Expand Down